/* * Copyright (c) 2015 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "analyze.hpp" #include "ast_render.hpp" #include "config.h" #include "error.hpp" #include "ir.hpp" #include "ir_print.hpp" #include "os.hpp" #include "parser.hpp" #include "softfloat.hpp" #include "zig_llvm.h" static const size_t default_backward_branch_quota = 1000; static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type); static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type); static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type); static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type); static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type); static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { if (node->owner->c_import_node != nullptr) { // if this happens, then translate_c generated code that // failed semantic analysis, which isn't supposed to happen ErrorMsg *err = add_node_error(g, node->owner->c_import_node, buf_sprintf("compiler bug: @cImport generated invalid zig code")); add_error_note(g, err, node, msg); g->errors.append(err); return err; } ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, node->owner->source_code, node->owner->line_offsets, msg); g->errors.append(err); return err; } ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { if (node->owner->c_import_node != nullptr) { // if this happens, then translate_c generated code that // failed semantic analysis, which isn't supposed to happen Buf *note_path = buf_create_from_str("?.c"); Buf *note_source = buf_create_from_str("TODO: remember C source location to display here "); ZigList note_line_offsets = {0}; note_line_offsets.append(0); ErrorMsg *note = err_msg_create_with_line(note_path, 0, 0, note_source, ¬e_line_offsets, msg); err_msg_add_note(parent_msg, note); return note; } ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, node->owner->source_code, node->owner->line_offsets, msg); err_msg_add_note(parent_msg, err); return err; } TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { TypeTableEntry *entry = allocate(1); entry->id = id; return entry; } static ScopeDecls **get_container_scope_ptr(TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdStruct) { return &type_entry->data.structure.decls_scope; } else if (type_entry->id == TypeTableEntryIdEnum) { return &type_entry->data.enumeration.decls_scope; } else if (type_entry->id == TypeTableEntryIdUnion) { return &type_entry->data.unionation.decls_scope; } zig_unreachable(); } ScopeDecls *get_container_scope(TypeTableEntry *type_entry) { return *get_container_scope_ptr(type_entry); } void init_scope(Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) { dest->id = id; dest->source_node = source_node; dest->parent = parent; } ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import) { assert(node == nullptr || node->type == NodeTypeRoot || node->type == NodeTypeContainerDecl || node->type == NodeTypeFnCallExpr); ScopeDecls *scope = allocate(1); init_scope(&scope->base, ScopeIdDecls, node, parent); scope->decl_table.init(4); scope->container_type = container_type; scope->import = import; return scope; } ScopeBlock *create_block_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeBlock); ScopeBlock *scope = allocate(1); init_scope(&scope->base, ScopeIdBlock, node, parent); scope->name = node->data.block.name; return scope; } ScopeDefer *create_defer_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeDefer); ScopeDefer *scope = allocate(1); init_scope(&scope->base, ScopeIdDefer, node, parent); return scope; } ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeDefer); ScopeDeferExpr *scope = allocate(1); init_scope(&scope->base, ScopeIdDeferExpr, node, parent); return scope; } Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) { ScopeVarDecl *scope = allocate(1); init_scope(&scope->base, ScopeIdVarDecl, node, parent); scope->var = var; return &scope->base; } ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeFnCallExpr); ScopeCImport *scope = allocate(1); init_scope(&scope->base, ScopeIdCImport, node, parent); buf_resize(&scope->buf, 0); return scope; } ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) { ScopeLoop *scope = allocate(1); init_scope(&scope->base, ScopeIdLoop, node, parent); if (node->type == NodeTypeWhileExpr) { scope->name = node->data.while_expr.name; } else if (node->type == NodeTypeForExpr) { scope->name = node->data.for_expr.name; } else { zig_unreachable(); } return scope; } ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeSuspend); ScopeSuspend *scope = allocate(1); init_scope(&scope->base, ScopeIdSuspend, node, parent); scope->name = node->data.suspend.name; return scope; } ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) { ScopeFnDef *scope = allocate(1); init_scope(&scope->base, ScopeIdFnDef, node, parent); scope->fn_entry = fn_entry; return scope; } Scope *create_comptime_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeCompTime || node->type == NodeTypeSwitchExpr); ScopeCompTime *scope = allocate(1); init_scope(&scope->base, ScopeIdCompTime, node, parent); return &scope->base; } Scope *create_coro_prelude_scope(AstNode *node, Scope *parent) { ScopeCoroPrelude *scope = allocate(1); init_scope(&scope->base, ScopeIdCoroPrelude, node, parent); return &scope->base; } ImportTableEntry *get_scope_import(Scope *scope) { while (scope) { if (scope->id == ScopeIdDecls) { ScopeDecls *decls_scope = (ScopeDecls *)scope; assert(decls_scope->import); return decls_scope->import; } scope = scope->parent; } zig_unreachable(); } static TypeTableEntry *new_container_type_entry(TypeTableEntryId id, AstNode *source_node, Scope *parent_scope) { TypeTableEntry *entry = new_type_table_entry(id); *get_container_scope_ptr(entry) = create_decls_scope(source_node, parent_scope, entry, get_scope_import(parent_scope)); return entry; } static uint8_t bits_needed_for_unsigned(uint64_t x) { if (x == 0) { return 0; } uint8_t base = log2_u64(x); uint64_t upper = (((uint64_t)1) << base) - 1; return (upper >= x) ? base : (base + 1); } bool type_is_complete(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdStruct: return type_entry->data.structure.complete; case TypeTableEntryIdEnum: return type_entry->data.enumeration.complete; case TypeTableEntryIdUnion: return type_entry->data.unionation.complete; case TypeTableEntryIdOpaque: return false; case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: return true; } zig_unreachable(); } bool type_has_zero_bits_known(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdStruct: return type_entry->data.structure.zero_bits_known; case TypeTableEntryIdEnum: return type_entry->data.enumeration.zero_bits_known; case TypeTableEntryIdUnion: return type_entry->data.unionation.zero_bits_known; case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: return true; } zig_unreachable(); } uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) { assert(type_is_complete(type_entry)); if (!type_has_bits(type_entry)) return 0; if (type_entry->id == TypeTableEntryIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { uint64_t size_in_bits = type_size_bits(g, type_entry); return (size_in_bits + 7) / 8; } else if (type_entry->id == TypeTableEntryIdArray) { TypeTableEntry *child_type = type_entry->data.array.child_type; if (child_type->id == TypeTableEntryIdStruct && child_type->data.structure.layout == ContainerLayoutPacked) { uint64_t size_in_bits = type_size_bits(g, type_entry); return (size_in_bits + 7) / 8; } } return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); } uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) { assert(type_is_complete(type_entry)); if (!type_has_bits(type_entry)) return 0; if (type_entry->id == TypeTableEntryIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { uint64_t result = 0; for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry); } return result; } else if (type_entry->id == TypeTableEntryIdArray) { TypeTableEntry *child_type = type_entry->data.array.child_type; if (child_type->id == TypeTableEntryIdStruct && child_type->data.structure.layout == ContainerLayoutPacked) { return type_entry->data.array.len * type_size_bits(g, child_type); } } return LLVMSizeOfTypeInBits(g->target_data_ref, type_entry->type_ref); } bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry) { type_ensure_zero_bits_known(g, type_entry); if (!type_has_bits(type_entry)) return true; if (!handle_is_ptr(type_entry)) return true; ensure_complete_type(g, type_entry); return type_entry->is_copyable; } static bool is_slice(TypeTableEntry *type) { return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice; } TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { return get_int_type(g, false, bits_needed_for_unsigned(x)); } TypeTableEntry *get_promise_type(CodeGen *g, TypeTableEntry *result_type) { if (result_type != nullptr && result_type->promise_parent != nullptr) { return result_type->promise_parent; } else if (result_type == nullptr && g->builtin_types.entry_promise != nullptr) { return g->builtin_types.entry_promise; } TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPromise); entry->type_ref = u8_ptr_type->type_ref; entry->zero_bits = false; entry->data.promise.result_type = result_type; buf_init_from_str(&entry->name, "promise"); if (result_type != nullptr) { buf_appendf(&entry->name, "->%s", buf_ptr(&result_type->name)); } entry->di_type = u8_ptr_type->di_type; if (result_type != nullptr) { result_type->promise_parent = entry; } else if (result_type == nullptr) { g->builtin_types.entry_promise = entry; } return entry; } TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) { assert(!type_is_invalid(child_type)); assert(ptr_len == PtrLenSingle || child_type->id != TypeTableEntryIdOpaque); TypeId type_id = {}; TypeTableEntry **parent_pointer = nullptr; uint32_t abi_alignment = get_abi_alignment(g, child_type); if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment || ptr_len != PtrLenSingle) { type_id.id = TypeTableEntryIdPointer; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; type_id.data.pointer.is_volatile = is_volatile; type_id.data.pointer.alignment = byte_alignment; type_id.data.pointer.bit_offset = bit_offset; type_id.data.pointer.unaligned_bit_count = unaligned_bit_count; type_id.data.pointer.ptr_len = ptr_len; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) return existing_entry->value; } else { assert(bit_offset == 0); parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)]; if (*parent_pointer) { assert((*parent_pointer)->data.pointer.alignment == byte_alignment); return *parent_pointer; } } type_ensure_zero_bits_known(g, child_type); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); entry->is_copyable = true; const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]"; const char *const_str = is_const ? "const " : ""; const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) { buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name)); } else if (unaligned_bit_count == 0) { buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment, const_str, volatile_str, buf_ptr(&child_type->name)); } else { buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment, bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } assert(child_type->id != TypeTableEntryIdInvalid); entry->zero_bits = !type_has_bits(child_type); if (!entry->zero_bits) { assert(byte_alignment > 0); if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment || ptr_len != PtrLenSingle) { TypeTableEntry *peer_type = get_pointer_to_type(g, child_type, false); entry->type_ref = peer_type->type_ref; entry->di_type = peer_type->di_type; } else { entry->type_ref = LLVMPointerType(child_type->type_ref, 0); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); assert(child_type->di_type); entry->di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, child_type->di_type, debug_size_in_bits, debug_align_in_bits, buf_ptr(&entry->name)); } } else { assert(byte_alignment == 0); entry->di_type = g->builtin_types.entry_void->di_type; } entry->data.pointer.ptr_len = ptr_len; entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; entry->data.pointer.is_volatile = is_volatile; entry->data.pointer.alignment = byte_alignment; entry->data.pointer.bit_offset = bit_offset; entry->data.pointer.unaligned_bit_count = unaligned_bit_count; if (parent_pointer) { *parent_pointer = entry; } else { g->type_table.put(type_id, entry); } return entry; } TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, get_abi_alignment(g, child_type), 0, 0); } TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) { if (return_type->promise_frame_parent != nullptr) { return return_type->promise_frame_parent; } TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise); TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); ZigList field_names = {}; field_names.append(AWAITER_HANDLE_FIELD_NAME); field_names.append(RESULT_FIELD_NAME); field_names.append(RESULT_PTR_FIELD_NAME); if (g->have_err_ret_tracing) { field_names.append(ERR_RET_TRACE_PTR_FIELD_NAME); field_names.append(ERR_RET_TRACE_FIELD_NAME); field_names.append(RETURN_ADDRESSES_FIELD_NAME); } ZigList field_types = {}; field_types.append(awaiter_handle_type); field_types.append(return_type); field_types.append(result_ptr_type); if (g->have_err_ret_tracing) { field_types.append(get_ptr_to_stack_trace_type(g)); field_types.append(g->stack_trace_type); field_types.append(get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count)); } assert(field_names.length == field_types.length); Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name)); TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names.items, field_types.items, field_names.length); return_type->promise_frame_parent = entry; return entry; } TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->maybe_parent) { TypeTableEntry *entry = child_type->maybe_parent; return entry; } else { ensure_complete_type(g, child_type); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional); assert(child_type->type_ref || child_type->zero_bits); entry->is_copyable = type_is_copyable(g, child_type); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); if (child_type->zero_bits) { entry->type_ref = LLVMInt1Type(); entry->di_type = g->builtin_types.entry_bool->di_type; } else if (type_is_codegen_pointer(child_type)) { assert(child_type->di_type); // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers // function types are technically pointers entry->type_ref = child_type->type_ref; entry->di_type = child_type->di_type; } else { assert(child_type->di_type); // create a struct with a boolean whether this is the null value LLVMTypeRef elem_types[] = { child_type->type_ref, LLVMInt1Type(), }; entry->type_ref = LLVMStructType(elem_types, 2, false); ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); ZigLLVMDIFile *di_file = nullptr; unsigned line = 0; entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), compile_unit_scope, di_file, line); uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref); uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref); uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); TypeTableEntry *bool_type = g->builtin_types.entry_bool; uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_type->type_ref); uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_type->type_ref); uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); ZigLLVMDIType *di_element_types[] = { ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "val", di_file, line, val_debug_size_in_bits, val_debug_align_in_bits, val_offset_in_bits, 0, child_type->di_type), ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "maybe", di_file, line, maybe_debug_size_in_bits, maybe_debug_align_in_bits, maybe_offset_in_bits, 0, bool_type->di_type), }; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 2, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; } entry->data.maybe.child_type = child_type; child_type->maybe_parent = entry; return entry; } } TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type) { assert(err_set_type->id == TypeTableEntryIdErrorSet); assert(!type_is_invalid(payload_type)); TypeId type_id = {}; type_id.id = TypeTableEntryIdErrorUnion; type_id.data.error_union.err_set_type = err_set_type; type_id.data.error_union.payload_type = payload_type; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) { return existing_entry->value; } TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); entry->is_copyable = true; assert(payload_type->di_type); ensure_complete_type(g, payload_type); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name)); entry->data.error_union.err_set_type = err_set_type; entry->data.error_union.payload_type = payload_type; if (!type_has_bits(payload_type)) { if (type_has_bits(err_set_type)) { entry->type_ref = err_set_type->type_ref; entry->di_type = err_set_type->di_type; g->error_di_types.append(&entry->di_type); } else { entry->zero_bits = true; entry->di_type = g->builtin_types.entry_void->di_type; } } else if (!type_has_bits(err_set_type)) { entry->type_ref = payload_type->type_ref; entry->di_type = payload_type->di_type; } else { LLVMTypeRef elem_types[] = { err_set_type->type_ref, payload_type->type_ref, }; entry->type_ref = LLVMStructType(elem_types, 2, false); ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); ZigLLVMDIFile *di_file = nullptr; unsigned line = 0; entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), compile_unit_scope, di_file, line); uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_type->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_type->type_ref); uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_err_index); uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_type->type_ref); uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_type->type_ref); uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_payload_index); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); ZigLLVMDIType *di_element_types[] = { ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "tag", di_file, line, tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, 0, err_set_type->di_type), ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "value", di_file, line, value_debug_size_in_bits, value_debug_align_in_bits, value_offset_in_bits, 0, payload_type->di_type), }; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 2, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; } g->type_table.put(type_id, entry); return entry; } TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) { TypeId type_id = {}; type_id.id = TypeTableEntryIdArray; type_id.data.array.child_type = child_type; type_id.data.array.size = array_size; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) { TypeTableEntry *entry = existing_entry->value; return entry; } ensure_complete_type(g, child_type); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); entry->zero_bits = (array_size == 0) || child_type->zero_bits; entry->is_copyable = false; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[%" ZIG_PRI_u64 "]%s", array_size, buf_ptr(&child_type->name)); if (entry->zero_bits) { entry->di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, 0, 0, child_type->di_type, 0); } else { entry->type_ref = child_type->type_ref ? LLVMArrayType(child_type->type_ref, (unsigned int)array_size) : nullptr; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); entry->di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, debug_size_in_bits, debug_align_in_bits, child_type->di_type, (int)array_size); } entry->data.array.child_type = child_type; entry->data.array.len = array_size; g->type_table.put(type_id, entry); return entry; } static void slice_type_common_init(CodeGen *g, TypeTableEntry *pointer_type, TypeTableEntry *entry) { unsigned element_count = 2; Buf *ptr_field_name = buf_create_from_str("ptr"); Buf *len_field_name = buf_create_from_str("len"); entry->data.structure.layout = ContainerLayoutAuto; entry->data.structure.is_slice = true; entry->data.structure.src_field_count = element_count; entry->data.structure.gen_field_count = element_count; entry->data.structure.fields = allocate(element_count); entry->data.structure.fields_by_name.init(element_count); entry->data.structure.fields[slice_ptr_index].name = ptr_field_name; entry->data.structure.fields[slice_ptr_index].type_entry = pointer_type; entry->data.structure.fields[slice_ptr_index].src_index = slice_ptr_index; entry->data.structure.fields[slice_ptr_index].gen_index = 0; entry->data.structure.fields[slice_len_index].name = len_field_name; entry->data.structure.fields[slice_len_index].type_entry = g->builtin_types.entry_usize; entry->data.structure.fields[slice_len_index].src_index = slice_len_index; entry->data.structure.fields[slice_len_index].gen_index = 1; entry->data.structure.fields_by_name.put(ptr_field_name, &entry->data.structure.fields[slice_ptr_index]); entry->data.structure.fields_by_name.put(len_field_name, &entry->data.structure.fields[slice_len_index]); assert(type_has_zero_bits_known(pointer_type->data.pointer.child_type)); if (pointer_type->data.pointer.child_type->zero_bits) { entry->data.structure.gen_field_count = 1; entry->data.structure.fields[slice_ptr_index].gen_index = SIZE_MAX; entry->data.structure.fields[slice_len_index].gen_index = 0; } } TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { assert(ptr_type->id == TypeTableEntryIdPointer); assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown); TypeTableEntry **parent_pointer = &ptr_type->data.pointer.slice_parent; if (*parent_pointer) { return *parent_pointer; } TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); entry->is_copyable = true; // replace the & with [] to go from a ptr type name to a slice type name buf_resize(&entry->name, 0); size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3; buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset); TypeTableEntry *child_type = ptr_type->data.pointer.child_type; uint32_t abi_alignment = get_abi_alignment(g, child_type); if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || ptr_type->data.pointer.alignment != abi_alignment) { TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, PtrLenUnknown, abi_alignment, 0, 0); TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); slice_type_common_init(g, ptr_type, entry); entry->type_ref = peer_slice_type->type_ref; entry->di_type = peer_slice_type->di_type; entry->data.structure.complete = true; entry->data.structure.zero_bits_known = true; entry->data.structure.abi_alignment = peer_slice_type->data.structure.abi_alignment; *parent_pointer = entry; return entry; } // If the child type is []const T then we need to make sure the type ref // and debug info is the same as if the child type were []T. if (is_slice(child_type)) { TypeTableEntry *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; assert(child_ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *grand_child_type = child_ptr_type->data.pointer.child_type; if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile || child_ptr_type->data.pointer.alignment != get_abi_alignment(g, grand_child_type)) { TypeTableEntry *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false, PtrLenUnknown, get_abi_alignment(g, grand_child_type), 0, 0); TypeTableEntry *bland_child_slice = get_slice_type(g, bland_child_ptr_type); TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false, PtrLenUnknown, get_abi_alignment(g, bland_child_slice), 0, 0); TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); entry->type_ref = peer_slice_type->type_ref; entry->di_type = peer_slice_type->di_type; entry->data.structure.abi_alignment = peer_slice_type->data.structure.abi_alignment; } } slice_type_common_init(g, ptr_type, entry); if (!entry->type_ref) { entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name)); ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); ZigLLVMDIFile *di_file = nullptr; unsigned line = 0; entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), compile_unit_scope, di_file, line); if (child_type->zero_bits) { LLVMTypeRef element_types[] = { g->builtin_types.entry_usize->type_ref, }; LLVMStructSetBody(entry->type_ref, element_types, 1, false); TypeTableEntry *usize_type = g->builtin_types.entry_usize; uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); ZigLLVMDIType *di_element_types[] = { ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "len", di_file, line, len_debug_size_in_bits, len_debug_align_in_bits, len_offset_in_bits, 0, usize_type->di_type), }; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 1, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; entry->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, usize_type->type_ref); } else { unsigned element_count = 2; LLVMTypeRef element_types[] = { ptr_type->type_ref, g->builtin_types.entry_usize->type_ref, }; LLVMStructSetBody(entry->type_ref, element_types, element_count, false); uint64_t ptr_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, ptr_type->type_ref); uint64_t ptr_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, ptr_type->type_ref); uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); TypeTableEntry *usize_type = g->builtin_types.entry_usize; uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); ZigLLVMDIType *di_element_types[] = { ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "ptr", di_file, line, ptr_debug_size_in_bits, ptr_debug_align_in_bits, ptr_offset_in_bits, 0, ptr_type->di_type), ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "len", di_file, line, len_debug_size_in_bits, len_debug_align_in_bits, len_offset_in_bits, 0, usize_type->di_type), }; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 2, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; entry->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); } } entry->data.structure.complete = true; entry->data.structure.zero_bits_known = true; *parent_pointer = entry; return entry; } TypeTableEntry *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOpaque); buf_init_from_str(&entry->name, name); ImportTableEntry *import = scope ? get_scope_import(scope) : nullptr; unsigned line = source_node ? (unsigned)(source_node->line + 1) : 0; entry->is_copyable = false; entry->type_ref = LLVMInt8Type(); entry->di_type = ZigLLVMCreateDebugForwardDeclType(g->dbuilder, ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), import ? ZigLLVMFileToScope(import->di_file) : nullptr, import ? import->di_file : nullptr, line); entry->zero_bits = false; return entry; } TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry) { TypeTableEntry *fn_type = fn_entry->type_entry; assert(fn_type->id == TypeTableEntryIdFn); if (fn_type->data.fn.bound_fn_parent) return fn_type->data.fn.bound_fn_parent; TypeTableEntry *bound_fn_type = new_type_table_entry(TypeTableEntryIdBoundFn); bound_fn_type->is_copyable = false; bound_fn_type->data.bound_fn.fn_type = fn_type; bound_fn_type->zero_bits = true; buf_resize(&bound_fn_type->name, 0); buf_appendf(&bound_fn_type->name, "(bound %s)", buf_ptr(&fn_type->name)); fn_type->data.fn.bound_fn_parent = bound_fn_type; return bound_fn_type; } bool calling_convention_does_first_arg_return(CallingConvention cc) { return cc == CallingConventionUnspecified; } static const char *calling_convention_name(CallingConvention cc) { switch (cc) { case CallingConventionUnspecified: return "undefined"; case CallingConventionC: return "ccc"; case CallingConventionCold: return "coldcc"; case CallingConventionNaked: return "nakedcc"; case CallingConventionStdcall: return "stdcallcc"; case CallingConventionAsync: return "async"; } zig_unreachable(); } static const char *calling_convention_fn_type_str(CallingConvention cc) { switch (cc) { case CallingConventionUnspecified: return ""; case CallingConventionC: return "extern "; case CallingConventionCold: return "coldcc "; case CallingConventionNaked: return "nakedcc "; case CallingConventionStdcall: return "stdcallcc "; case CallingConventionAsync: return "async "; } zig_unreachable(); } static bool calling_convention_allows_zig_types(CallingConvention cc) { switch (cc) { case CallingConventionUnspecified: case CallingConventionAsync: return true; case CallingConventionC: case CallingConventionCold: case CallingConventionNaked: case CallingConventionStdcall: return false; } zig_unreachable(); } TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) { if (g->stack_trace_type == nullptr) { ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace"); assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType); g->stack_trace_type = stack_trace_type_val->data.x_type; g->ptr_to_stack_trace_type = get_pointer_to_type(g, g->stack_trace_type, false); } return g->ptr_to_stack_trace_type; } TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { auto table_entry = g->fn_type_table.maybe_get(fn_type_id); if (table_entry) { return table_entry->value; } if (fn_type_id->return_type != nullptr) { ensure_complete_type(g, fn_type_id->return_type); if (type_is_invalid(fn_type_id->return_type)) return g->builtin_types.entry_invalid; } else { zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"); } TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); fn_type->is_copyable = true; fn_type->data.fn.fn_type_id = *fn_type_id; bool skip_debug_info = false; // populate the name of the type buf_resize(&fn_type->name, 0); if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) { assert(fn_type_id->async_allocator_type != nullptr); buf_appendf(&fn_type->name, "async<%s> ", buf_ptr(&fn_type_id->async_allocator_type->name)); } else { const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc); buf_appendf(&fn_type->name, "%s", cc_str); } buf_appendf(&fn_type->name, "fn("); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; TypeTableEntry *param_type = param_info->type; const char *comma = (i == 0) ? "" : ", "; const char *noalias_str = param_info->is_noalias ? "noalias " : ""; buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(¶m_type->name)); skip_debug_info = skip_debug_info || !param_type->di_type; } if (fn_type_id->is_var_args) { const char *comma = (fn_type_id->param_count == 0) ? "" : ", "; buf_appendf(&fn_type->name, "%s...", comma); } buf_appendf(&fn_type->name, ")"); if (fn_type_id->alignment != 0) { buf_appendf(&fn_type->name, " align(%" PRIu32 ")", fn_type_id->alignment); } buf_appendf(&fn_type->name, " %s", buf_ptr(&fn_type_id->return_type->name)); skip_debug_info = skip_debug_info || !fn_type_id->return_type->di_type; // next, loop over the parameters again and compute debug information // and codegen information if (!skip_debug_info) { bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && handle_is_ptr(fn_type_id->return_type); bool is_async = fn_type_id->cc == CallingConventionAsync; bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); // +1 for maybe making the first argument the return value // +1 for maybe first argument the error return trace // +2 for maybe arguments async allocator and error code pointer LLVMTypeRef *gen_param_types = allocate(4 + fn_type_id->param_count); // +1 because 0 is the return type and // +1 for maybe making first arg ret val and // +1 for maybe first argument the error return trace // +2 for maybe arguments async allocator and error code pointer ZigLLVMDIType **param_di_types = allocate(5 + fn_type_id->param_count); param_di_types[0] = fn_type_id->return_type->di_type; size_t gen_param_index = 0; TypeTableEntry *gen_return_type; if (is_async) { gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); } else if (!type_has_bits(fn_type_id->return_type)) { gen_return_type = g->builtin_types.entry_void; } else if (first_arg_return) { TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false); gen_param_types[gen_param_index] = gen_type->type_ref; gen_param_index += 1; // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; gen_return_type = g->builtin_types.entry_void; } else { gen_return_type = fn_type_id->return_type; } fn_type->data.fn.gen_return_type = gen_return_type; if (prefix_arg_error_return_trace) { TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g); gen_param_types[gen_param_index] = gen_type->type_ref; gen_param_index += 1; // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; } if (is_async) { { // async allocator param TypeTableEntry *gen_type = fn_type_id->async_allocator_type; gen_param_types[gen_param_index] = gen_type->type_ref; gen_param_index += 1; // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; } { // error code pointer TypeTableEntry *gen_type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false); gen_param_types[gen_param_index] = gen_type->type_ref; gen_param_index += 1; // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; } } fn_type->data.fn.gen_param_info = allocate(fn_type_id->param_count); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i]; TypeTableEntry *type_entry = src_param_info->type; FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i]; gen_param_info->src_index = i; gen_param_info->gen_index = SIZE_MAX; ensure_complete_type(g, type_entry); if (type_is_invalid(type_entry)) return g->builtin_types.entry_invalid; if (type_has_bits(type_entry)) { TypeTableEntry *gen_type; if (handle_is_ptr(type_entry)) { gen_type = get_pointer_to_type(g, type_entry, true); gen_param_info->is_byval = true; } else { gen_type = type_entry; } gen_param_types[gen_param_index] = gen_type->type_ref; gen_param_info->gen_index = gen_param_index; gen_param_info->type = gen_type; gen_param_index += 1; // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; } } fn_type->data.fn.gen_param_count = gen_param_index; fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, gen_param_types, (unsigned int)gen_param_index, fn_type_id->is_var_args); fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types, (int)(gen_param_index + 1), 0); } g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type); return fn_type; } static TypeTableEntryId container_to_type(ContainerKind kind) { switch (kind) { case ContainerKindStruct: return TypeTableEntryIdStruct; case ContainerKindEnum: return TypeTableEntryIdEnum; case ContainerKindUnion: return TypeTableEntryIdUnion; } zig_unreachable(); } TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, AstNode *decl_node, const char *name, ContainerLayout layout) { TypeTableEntryId type_id = container_to_type(kind); TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, scope); switch (kind) { case ContainerKindStruct: entry->data.structure.decl_node = decl_node; entry->data.structure.layout = layout; break; case ContainerKindEnum: entry->data.enumeration.decl_node = decl_node; entry->data.enumeration.layout = layout; break; case ContainerKindUnion: entry->data.unionation.decl_node = decl_node; entry->data.unionation.layout = layout; break; } size_t line = decl_node ? decl_node->line : 0; unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); ImportTableEntry *import = get_scope_import(scope); entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name); entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, dwarf_kind, name, ZigLLVMFileToScope(import->di_file), import->di_file, (unsigned)(line + 1)); buf_init_from_str(&entry->name, name); return entry; } static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, TypeTableEntry *type_entry, Buf *type_name) { size_t backward_branch_count = 0; return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota, nullptr, nullptr, node, type_name, nullptr); } TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); if (result->value.type->id == TypeTableEntryIdInvalid) return g->builtin_types.entry_invalid; assert(result->value.special != ConstValSpecialRuntime); return result->value.data.x_type; } TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); fn_type->is_copyable = false; buf_resize(&fn_type->name, 0); if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) { const char *async_allocator_type_str = (fn_type->data.fn.fn_type_id.async_allocator_type == nullptr) ? "var" : buf_ptr(&fn_type_id->async_allocator_type->name); buf_appendf(&fn_type->name, "async(%s) ", async_allocator_type_str); } else { const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc); buf_appendf(&fn_type->name, "%s", cc_str); } buf_appendf(&fn_type->name, "fn("); size_t i = 0; for (; i < fn_type_id->next_param_index; i += 1) { const char *comma_str = (i == 0) ? "" : ","; buf_appendf(&fn_type->name, "%s%s", comma_str, buf_ptr(&fn_type_id->param_info[i].type->name)); } for (; i < fn_type_id->param_count; i += 1) { const char *comma_str = (i == 0) ? "" : ","; buf_appendf(&fn_type->name, "%svar", comma_str); } buf_appendf(&fn_type->name, ")var"); fn_type->data.fn.fn_type_id = *fn_type_id; fn_type->data.fn.is_generic = true; fn_type->zero_bits = true; return fn_type; } void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; if (fn_proto->cc == CallingConventionUnspecified) { bool extern_abi = fn_proto->is_extern || fn_proto->is_export; fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified; } else { fn_type_id->cc = fn_proto->cc; } fn_type_id->param_count = fn_proto->params.length; fn_type_id->param_info = allocate(param_count_alloc); fn_type_id->next_param_index = 0; fn_type_id->is_var_args = fn_proto->is_var_args; } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { IrInstruction *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); if (type_is_invalid(align_result->value.type)) return false; uint32_t align_bytes = bigint_as_unsigned(&align_result->value.data.x_bigint); if (align_bytes == 0) { add_node_error(g, node, buf_sprintf("alignment must be >= 1")); return false; } if (!is_power_of_2(align_bytes)) { add_node_error(g, node, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); return false; } *result = align_bytes; return true; } static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) { TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, ptr_type); IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); if (type_is_invalid(instr->value.type)) return false; ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index]; ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; expand_undef_array(g, array_val); size_t len = bigint_as_unsigned(&len_field->data.x_bigint); Buf *result = buf_alloc(); buf_resize(result, len); for (size_t i = 0; i < len; i += 1) { size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i; ConstExprValue *char_val = &array_val->data.x_array.s_none.elements[new_index]; if (char_val->special == ConstValSpecialUndef) { add_node_error(g, node, buf_sprintf("use of undefined value")); return false; } uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint); assert(big_c <= UINT8_MAX); uint8_t c = (uint8_t)big_c; buf_ptr(result)[i] = c; } *out_buffer = result; return true; } static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: return false; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdFn: return true; case TypeTableEntryIdStruct: return type_entry->data.structure.layout == ContainerLayoutPacked; case TypeTableEntryIdUnion: return type_entry->data.unionation.layout == ContainerLayoutPacked; case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; return type_is_codegen_pointer(child_type); } case TypeTableEntryIdEnum: return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr; } zig_unreachable(); } static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: return false; case TypeTableEntryIdOpaque: case TypeTableEntryIdUnreachable: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: return true; case TypeTableEntryIdInt: switch (type_entry->data.integral.bit_count) { case 8: case 16: case 32: case 64: case 128: return true; default: return false; } case TypeTableEntryIdFloat: return true; case TypeTableEntryIdArray: return type_allowed_in_extern(g, type_entry->data.array.child_type); case TypeTableEntryIdFn: return type_entry->data.fn.fn_type_id.cc == CallingConventionC; case TypeTableEntryIdPointer: return type_allowed_in_extern(g, type_entry->data.pointer.child_type); case TypeTableEntryIdStruct: return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; } case TypeTableEntryIdEnum: return type_entry->data.enumeration.layout == ContainerLayoutExtern || type_entry->data.enumeration.layout == ContainerLayoutPacked; case TypeTableEntryIdUnion: return type_entry->data.unionation.layout == ContainerLayoutExtern || type_entry->data.unionation.layout == ContainerLayoutPacked; } zig_unreachable(); } TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) { TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "@typeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name)); err_set_type->is_copyable = true; err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; err_set_type->data.error_set.err_count = 0; err_set_type->data.error_set.errors = nullptr; err_set_type->data.error_set.infer_fn = fn_entry; g->error_di_types.append(&err_set_type->di_type); return err_set_type; } static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, FnTableEntry *fn_entry) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; FnTypeId fn_type_id = {0}; init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length); for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); assert(param_node->type == NodeTypeParamDecl); bool param_is_comptime = param_node->data.param_decl.is_inline; bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_comptime) { if (!calling_convention_allows_zig_types(fn_type_id.cc)) { add_node_error(g, param_node, buf_sprintf("comptime parameter not allowed in function with calling convention '%s'", calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } return get_generic_fn_type(g, &fn_type_id); } else if (param_is_var_args) { if (fn_type_id.cc == CallingConventionC) { fn_type_id.param_count = fn_type_id.next_param_index; continue; } else if (calling_convention_allows_zig_types(fn_type_id.cc)) { return get_generic_fn_type(g, &fn_type_id); } else { add_node_error(g, param_node, buf_sprintf("var args not allowed in function with calling convention '%s'", calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } } else if (param_node->data.param_decl.var_token != nullptr) { if (!calling_convention_allows_zig_types(fn_type_id.cc)) { add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'", calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } return get_generic_fn_type(g, &fn_type_id); } TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); if (type_is_invalid(type_entry)) { return g->builtin_types.entry_invalid; } if (!calling_convention_allows_zig_types(fn_type_id.cc)) { type_ensure_zero_bits_known(g, type_entry); if (!type_has_bits(type_entry)) { add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } } if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, type_entry)) { add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'", buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } switch (type_entry->id) { case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' must be declared comptime", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: break; } FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; param_info->type = type_entry; param_info->is_noalias = param_node->data.param_decl.is_noalias; } if (fn_proto->align_expr != nullptr) { if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &fn_type_id.alignment)) { return g->builtin_types.entry_invalid; } } if (fn_proto->return_var_token != nullptr) { if (!calling_convention_allows_zig_types(fn_type_id.cc)) { add_node_error(g, fn_proto->return_type, buf_sprintf("return type 'var' not allowed in function with calling convention '%s'", calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } add_node_error(g, proto_node, buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); return g->builtin_types.entry_invalid; //return get_generic_fn_type(g, &fn_type_id); } TypeTableEntry *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type); if (type_is_invalid(specified_return_type)) { fn_type_id.return_type = g->builtin_types.entry_invalid; return g->builtin_types.entry_invalid; } if (fn_proto->auto_err_set) { TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(g, fn_entry); fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type); } else { fn_type_id.return_type = specified_return_type; } if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, fn_type_id.return_type)) { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", buf_ptr(&fn_type_id.return_type->name), calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } switch (fn_type_id.return_type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name))); return g->builtin_types.entry_invalid; case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: if (!calling_convention_allows_zig_types(fn_type_id.cc)) { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", buf_ptr(&fn_type_id.return_type->name), calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } return get_generic_fn_type(g, &fn_type_id); case TypeTableEntryIdUnreachable: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: break; } if (fn_type_id.cc == CallingConventionAsync) { if (fn_proto->async_allocator_type == nullptr) { return get_generic_fn_type(g, &fn_type_id); } fn_type_id.async_allocator_type = analyze_type_expr(g, child_scope, fn_proto->async_allocator_type); if (type_is_invalid(fn_type_id.async_allocator_type)) { return g->builtin_types.entry_invalid; } } return get_fn_type(g, &fn_type_id); } bool type_is_invalid(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: return true; case TypeTableEntryIdStruct: return type_entry->data.structure.is_invalid; case TypeTableEntryIdEnum: return type_entry->data.enumeration.is_invalid; case TypeTableEntryIdUnion: return type_entry->data.unionation.is_invalid; default: return false; } zig_unreachable(); } static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { assert(enum_type->id == TypeTableEntryIdEnum); if (enum_type->data.enumeration.complete) return; resolve_enum_zero_bits(g, enum_type); if (type_is_invalid(enum_type)) return; AstNode *decl_node = enum_type->data.enumeration.decl_node; if (enum_type->data.enumeration.embedded_in_current) { if (!enum_type->data.enumeration.reported_infinite_err) { enum_type->data.enumeration.reported_infinite_err = true; add_node_error(g, decl_node, buf_sprintf("enum '%s' contains itself", buf_ptr(&enum_type->name))); } return; } assert(!enum_type->data.enumeration.zero_bits_loop_flag); assert(decl_node->type == NodeTypeContainerDecl); assert(enum_type->di_type); uint32_t field_count = enum_type->data.enumeration.src_field_count; assert(enum_type->data.enumeration.fields); ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); Scope *scope = &enum_type->data.enumeration.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); // set temporary flag enum_type->data.enumeration.embedded_in_current = true; for (uint32_t i = 0; i < field_count; i += 1) { TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i]; // TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t // http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name), bigint_as_signed(&enum_field->value)); } // unset temporary flag enum_type->data.enumeration.embedded_in_current = false; enum_type->data.enumeration.complete = true; if (enum_type->data.enumeration.is_invalid) return; if (enum_type->zero_bits) { enum_type->type_ref = LLVMVoidType(); uint64_t debug_size_in_bits = 0; uint64_t debug_align_in_bits = 0; ZigLLVMDIType **di_root_members = nullptr; size_t debug_member_count = 0; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), import->di_file, (unsigned)(decl_node->line + 1), debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_root_members, (int)debug_member_count, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); enum_type->di_type = replacement_di_type; return; } TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; // create debug type for tag uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_int_type->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), import->di_file, (unsigned)(decl_node->line + 1), tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, tag_int_type->di_type, ""); ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); enum_type->di_type = tag_di_type; } TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[], TypeTableEntry *field_types[], size_t field_count) { TypeTableEntry *struct_type = new_type_table_entry(TypeTableEntryIdStruct); buf_init_from_str(&struct_type->name, type_name); struct_type->data.structure.src_field_count = field_count; struct_type->data.structure.gen_field_count = 0; struct_type->data.structure.zero_bits_known = true; struct_type->data.structure.complete = true; struct_type->data.structure.fields = allocate(field_count); struct_type->data.structure.fields_by_name.init(field_count); ZigLLVMDIType **di_element_types = allocate(field_count); LLVMTypeRef *element_types = allocate(field_count); for (size_t i = 0; i < field_count; i += 1) { element_types[struct_type->data.structure.gen_field_count] = field_types[i]->type_ref; TypeStructField *field = &struct_type->data.structure.fields[i]; field->name = buf_create_from_str(field_names[i]); field->type_entry = field_types[i]; field->src_index = i; if (type_has_bits(field->type_entry)) { field->gen_index = struct_type->data.structure.gen_field_count; struct_type->data.structure.gen_field_count += 1; } else { field->gen_index = SIZE_MAX; } auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field); assert(prev_entry == nullptr); } struct_type->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), type_name); LLVMStructSetBody(struct_type->type_ref, element_types, struct_type->data.structure.gen_field_count, false); struct_type->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, ZigLLVMTag_DW_structure_type(), type_name, ZigLLVMCompileUnitToScope(g->compile_unit), nullptr, 0); for (size_t i = 0; i < field_count; i += 1) { TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; if (type_struct_field->gen_index == SIZE_MAX) { continue; } TypeTableEntry *field_type = type_struct_field->type_entry; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, type_struct_field->gen_index); di_element_types[type_struct_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), nullptr, 0, debug_size_in_bits, debug_align_in_bits, debug_offset_in_bits, 0, field_type->di_type); assert(di_element_types[type_struct_field->gen_index]); } uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, struct_type->type_ref); ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, ZigLLVMCompileUnitToScope(g->compile_unit), type_name, nullptr, 0, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, struct_type->data.structure.gen_field_count, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type); struct_type->di_type = replacement_di_type; struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, struct_type->type_ref); return struct_type; } static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { assert(struct_type->id == TypeTableEntryIdStruct); if (struct_type->data.structure.complete) return; resolve_struct_zero_bits(g, struct_type); if (struct_type->data.structure.is_invalid) return; AstNode *decl_node = struct_type->data.structure.decl_node; if (struct_type->data.structure.embedded_in_current) { struct_type->data.structure.is_invalid = true; if (!struct_type->data.structure.reported_infinite_err) { struct_type->data.structure.reported_infinite_err = true; add_node_error(g, decl_node, buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name))); } return; } assert(!struct_type->data.structure.zero_bits_loop_flag); assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0); assert(decl_node->type == NodeTypeContainerDecl); size_t field_count = struct_type->data.structure.src_field_count; size_t gen_field_count = struct_type->data.structure.gen_field_count; LLVMTypeRef *element_types = allocate(gen_field_count); // this field should be set to true only during the recursive calls to resolve_struct_type struct_type->data.structure.embedded_in_current = true; Scope *scope = &struct_type->data.structure.decls_scope->base; size_t gen_field_index = 0; bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked); size_t packed_bits_offset = 0; size_t first_packed_bits_offset_misalign = SIZE_MAX; size_t debug_field_count = 0; for (size_t i = 0; i < field_count; i += 1) { TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; TypeTableEntry *field_type = type_struct_field->type_entry; ensure_complete_type(g, field_type); if (type_is_invalid(field_type)) { struct_type->data.structure.is_invalid = true; break; } if (!type_has_bits(field_type)) continue; type_struct_field->gen_index = gen_field_index; if (packed) { if (!type_allowed_in_packed_struct(field_type)) { AstNode *field_source_node = decl_node->data.container_decl.fields.at(i); add_node_error(g, field_source_node, buf_sprintf("packed structs cannot contain fields of type '%s'", buf_ptr(&field_type->name))); struct_type->data.structure.is_invalid = true; break; } size_t field_size_in_bits = type_size_bits(g, field_type); size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits; type_struct_field->packed_bits_size = field_size_in_bits; if (first_packed_bits_offset_misalign != SIZE_MAX) { // this field is not byte-aligned; it is part of the previous field with a bit offset type_struct_field->packed_bits_offset = packed_bits_offset - first_packed_bits_offset_misalign; type_struct_field->unaligned_bit_count = field_size_in_bits; size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign; LLVMTypeRef int_type_ref = LLVMIntType((unsigned)(full_bit_count)); if (8 * LLVMStoreSizeOfType(g->target_data_ref, int_type_ref) == full_bit_count) { // next field recovers store alignment element_types[gen_field_index] = int_type_ref; gen_field_index += 1; first_packed_bits_offset_misalign = SIZE_MAX; } } else if (8 * LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref) != field_size_in_bits) { first_packed_bits_offset_misalign = packed_bits_offset; type_struct_field->packed_bits_offset = 0; type_struct_field->unaligned_bit_count = field_size_in_bits; } else { // This is a byte-aligned field (both start and end) in a packed struct. element_types[gen_field_index] = field_type->type_ref; type_struct_field->packed_bits_offset = 0; type_struct_field->unaligned_bit_count = 0; gen_field_index += 1; } packed_bits_offset = next_packed_bits_offset; } else { element_types[gen_field_index] = field_type->type_ref; assert(element_types[gen_field_index]); gen_field_index += 1; } debug_field_count += 1; } if (first_packed_bits_offset_misalign != SIZE_MAX) { size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign; LLVMTypeRef int_type_ref = LLVMIntType((unsigned)full_bit_count); size_t store_bit_count = 8 * LLVMStoreSizeOfType(g->target_data_ref, int_type_ref); element_types[gen_field_index] = LLVMIntType((unsigned)store_bit_count); gen_field_index += 1; } struct_type->data.structure.embedded_in_current = false; struct_type->data.structure.complete = true; if (struct_type->data.structure.is_invalid) return; if (struct_type->zero_bits) { struct_type->type_ref = LLVMVoidType(); ImportTableEntry *import = get_scope_import(scope); uint64_t debug_size_in_bits = 0; uint64_t debug_align_in_bits = 0; ZigLLVMDIType **di_element_types = nullptr; size_t debug_field_count = 0; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&struct_type->name), import->di_file, (unsigned)(decl_node->line + 1), debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type); struct_type->di_type = replacement_di_type; return; } assert(struct_type->di_type); // the count may have been adjusting from packing bit fields gen_field_count = gen_field_index; struct_type->data.structure.gen_field_count = (uint32_t)gen_field_count; LLVMStructSetBody(struct_type->type_ref, element_types, (unsigned)gen_field_count, packed); // if you hit this assert then probably this type or a related type didn't // get ensure_complete_type called on it before using it with something that // requires a complete type assert(LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref) > 0); ZigLLVMDIType **di_element_types = allocate(debug_field_count); ImportTableEntry *import = get_scope_import(scope); size_t debug_field_index = 0; for (size_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; size_t gen_field_index = type_struct_field->gen_index; if (gen_field_index == SIZE_MAX) { continue; } TypeTableEntry *field_type = type_struct_field->type_entry; // if the field is a function, actually the debug info should be a pointer. ZigLLVMDIType *field_di_type; if (field_type->id == TypeTableEntryIdFn) { TypeTableEntry *field_ptr_type = get_pointer_to_type(g, field_type, true); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_ptr_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_ptr_type->type_ref); field_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, field_type->di_type, debug_size_in_bits, debug_align_in_bits, buf_ptr(&field_ptr_type->name)); } else { field_di_type = field_type->di_type; } assert(field_type->type_ref); assert(struct_type->type_ref); assert(struct_type->data.structure.complete); uint64_t debug_size_in_bits; uint64_t debug_align_in_bits; uint64_t debug_offset_in_bits; if (packed) { debug_size_in_bits = type_struct_field->packed_bits_size; debug_align_in_bits = 1; debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, (unsigned)gen_field_index) + type_struct_field->packed_bits_offset; } else { debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref); debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, (unsigned)gen_field_index); } di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), import->di_file, (unsigned)(field_node->line + 1), debug_size_in_bits, debug_align_in_bits, debug_offset_in_bits, 0, field_di_type); assert(di_element_types[debug_field_index]); debug_field_index += 1; } uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, struct_type->type_ref); ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&struct_type->name), import->di_file, (unsigned)(decl_node->line + 1), debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type); struct_type->di_type = replacement_di_type; } static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { assert(union_type->id == TypeTableEntryIdUnion); if (union_type->data.unionation.complete) return; resolve_union_zero_bits(g, union_type); if (type_is_invalid(union_type)) return; AstNode *decl_node = union_type->data.unionation.decl_node; if (union_type->data.unionation.embedded_in_current) { if (!union_type->data.unionation.reported_infinite_err) { union_type->data.unionation.reported_infinite_err = true; union_type->data.unionation.is_invalid = true; add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name))); } return; } assert(!union_type->data.unionation.zero_bits_loop_flag); assert(decl_node->type == NodeTypeContainerDecl); assert(union_type->di_type); uint32_t field_count = union_type->data.unionation.src_field_count; assert(union_type->data.unionation.fields); uint32_t gen_field_count = union_type->data.unionation.gen_field_count; ZigLLVMDIType **union_inner_di_types = allocate(gen_field_count); TypeTableEntry *most_aligned_union_member = nullptr; uint64_t size_of_most_aligned_member_in_bits = 0; uint64_t biggest_align_in_bits = 0; uint64_t biggest_size_in_bits = 0; Scope *scope = &union_type->data.unionation.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); // set temporary flag union_type->data.unionation.embedded_in_current = true; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeUnionField *union_field = &union_type->data.unionation.fields[i]; TypeTableEntry *field_type = union_field->type_entry; ensure_complete_type(g, field_type); if (type_is_invalid(field_type)) { union_type->data.unionation.is_invalid = true; continue; } if (!type_has_bits(field_type)) continue; uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); assert(store_size_in_bits > 0); assert(abi_align_in_bits > 0); union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), buf_ptr(union_field->enum_field->name), import->di_file, (unsigned)(field_node->line + 1), store_size_in_bits, abi_align_in_bits, 0, 0, field_type->di_type); biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits); if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) { most_aligned_union_member = field_type; biggest_align_in_bits = abi_align_in_bits; size_of_most_aligned_member_in_bits = store_size_in_bits; } } // unset temporary flag union_type->data.unionation.embedded_in_current = false; union_type->data.unionation.complete = true; union_type->data.unionation.union_size_bytes = biggest_size_in_bits / 8; union_type->data.unionation.most_aligned_union_member = most_aligned_union_member; if (union_type->data.unionation.is_invalid) return; if (union_type->zero_bits) { union_type->type_ref = LLVMVoidType(); uint64_t debug_size_in_bits = 0; uint64_t debug_align_in_bits = 0; ZigLLVMDIType **di_root_members = nullptr; size_t debug_member_count = 0; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name), import->di_file, (unsigned)(decl_node->line + 1), debug_size_in_bits, debug_align_in_bits, 0, di_root_members, (int)debug_member_count, 0, ""); ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type); union_type->di_type = replacement_di_type; return; } uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; TypeTableEntry *tag_type = union_type->data.unionation.tag_type; if (tag_type == nullptr || tag_type->zero_bits) { assert(most_aligned_union_member != nullptr); if (padding_in_bits > 0) { TypeTableEntry *u8_type = get_int_type(g, false, 8); TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); LLVMTypeRef union_element_types[] = { most_aligned_union_member->type_ref, padding_array->type_ref, }; LLVMStructSetBody(union_type->type_ref, union_element_types, 2, false); } else { LLVMStructSetBody(union_type->type_ref, &most_aligned_union_member->type_ref, 1, false); } union_type->data.unionation.union_type_ref = union_type->type_ref; union_type->data.unionation.gen_tag_index = SIZE_MAX; union_type->data.unionation.gen_union_index = SIZE_MAX; assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type->type_ref) >= biggest_align_in_bits); assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref) >= biggest_size_in_bits); // create debug type for union ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name), import->di_file, (unsigned)(decl_node->line + 1), biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, gen_field_count, 0, ""); ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type); union_type->di_type = replacement_di_type; return; } LLVMTypeRef union_type_ref; if (padding_in_bits > 0) { TypeTableEntry *u8_type = get_int_type(g, false, 8); TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); LLVMTypeRef union_element_types[] = { most_aligned_union_member->type_ref, padding_array->type_ref, }; union_type_ref = LLVMStructType(union_element_types, 2, false); } else if (most_aligned_union_member == nullptr) { union_type->data.unionation.gen_tag_index = SIZE_MAX; union_type->data.unionation.gen_union_index = SIZE_MAX; union_type->type_ref = tag_type->type_ref; ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, tag_type->di_type); union_type->di_type = tag_type->di_type; return; } else { union_type_ref = most_aligned_union_member->type_ref; } union_type->data.unionation.union_type_ref = union_type_ref; assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); // create llvm type for root struct TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); if (align_of_tag_in_bits >= biggest_align_in_bits) { union_type->data.unionation.gen_tag_index = 0; union_type->data.unionation.gen_union_index = 1; } else { union_type->data.unionation.gen_union_index = 0; union_type->data.unionation.gen_tag_index = 1; } LLVMTypeRef root_struct_element_types[2]; root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type->type_ref; root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref; LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false); // create debug type for union ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "AnonUnion", import->di_file, (unsigned)(decl_node->line + 1), biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, gen_field_count, 0, ""); uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref, union_type->data.unionation.gen_union_index); uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref, union_type->data.unionation.gen_tag_index); ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "payload", import->di_file, (unsigned)(decl_node->line + 1), biggest_size_in_bits, biggest_align_in_bits, union_offset_in_bits, 0, union_di_type); uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "tag", import->di_file, (unsigned)(decl_node->line + 1), tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, 0, tag_type->di_type); ZigLLVMDIType *di_root_members[2]; di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type; di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, union_type->type_ref); ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name), import->di_file, (unsigned)(decl_node->line + 1), debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_root_members, 2, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type); union_type->di_type = replacement_di_type; } static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { assert(enum_type->id == TypeTableEntryIdEnum); if (enum_type->data.enumeration.zero_bits_known) return; if (enum_type->data.enumeration.zero_bits_loop_flag) { enum_type->data.enumeration.zero_bits_known = true; enum_type->data.enumeration.zero_bits_loop_flag = false; return; } enum_type->data.enumeration.zero_bits_loop_flag = true; AstNode *decl_node = enum_type->data.enumeration.decl_node; assert(decl_node->type == NodeTypeContainerDecl); assert(enum_type->di_type); assert(!enum_type->data.enumeration.fields); uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; if (field_count == 0) { add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); enum_type->data.enumeration.src_field_count = field_count; enum_type->data.enumeration.fields = nullptr; enum_type->data.enumeration.is_invalid = true; enum_type->data.enumeration.zero_bits_loop_flag = false; enum_type->data.enumeration.zero_bits_known = true; return; } enum_type->data.enumeration.src_field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); enum_type->data.enumeration.fields_by_name.init(field_count); Scope *scope = &enum_type->data.enumeration.decls_scope->base; HashMap occupied_tag_values = {}; occupied_tag_values.init(field_count); TypeTableEntry *tag_int_type; if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { tag_int_type = get_c_int_type(g, CIntTypeInt); } else { tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); } // TODO: Are extern enums allowed to have an init_arg_expr? if (decl_node->data.container_decl.init_arg_expr != nullptr) { TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); if (type_is_invalid(wanted_tag_int_type)) { enum_type->data.enumeration.is_invalid = true; } else if (wanted_tag_int_type->id != TypeTableEntryIdInt) { enum_type->data.enumeration.is_invalid = true; add_node_error(g, decl_node->data.container_decl.init_arg_expr, buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); } else if (wanted_tag_int_type->data.integral.is_signed) { enum_type->data.enumeration.is_invalid = true; add_node_error(g, decl_node->data.container_decl.init_arg_expr, buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) { enum_type->data.enumeration.is_invalid = true; add_node_error(g, decl_node->data.container_decl.init_arg_expr, buf_sprintf("'%s' too small to hold all bits; must be at least '%s'", buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name))); } else { tag_int_type = wanted_tag_int_type; } } enum_type->data.enumeration.tag_int_type = tag_int_type; enum_type->type_ref = tag_int_type->type_ref; for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; type_enum_field->name = field_node->data.struct_field.name; type_enum_field->decl_index = field_i; type_enum_field->decl_node = field_node; if (field_node->data.struct_field.type != nullptr) { ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, buf_sprintf("structs and unions, not enums, support field types")); add_error_note(g, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); } auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); if (field_entry != nullptr) { ErrorMsg *msg = add_node_error(g, field_node, buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); enum_type->data.enumeration.is_invalid = true; continue; } AstNode *tag_value = field_node->data.struct_field.value; // In this first pass we resolve explicit tag values. // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); if (result_inst->value.type->id == TypeTableEntryIdInvalid) { enum_type->data.enumeration.is_invalid = true; continue; } assert(result_inst->value.special != ConstValSpecialRuntime); assert(result_inst->value.type->id == TypeTableEntryIdInt); auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); if (entry == nullptr) { bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint); } else { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); add_error_note(g, msg, entry->value, buf_sprintf("other occurrence here")); enum_type->data.enumeration.is_invalid = true; continue; } } } // Now iterate again and populate the unspecified tag values uint32_t next_maybe_unoccupied_index = 0; for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; AstNode *tag_value = field_node->data.struct_field.value; if (tag_value == nullptr) { if (occupied_tag_values.size() == 0) { bigint_init_unsigned(&type_enum_field->value, next_maybe_unoccupied_index); next_maybe_unoccupied_index += 1; } else { BigInt proposed_value; for (;;) { bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); next_maybe_unoccupied_index += 1; auto entry = occupied_tag_values.put_unique(proposed_value, field_node); if (entry != nullptr) { continue; } break; } bigint_init_bigint(&type_enum_field->value, &proposed_value); } } } enum_type->data.enumeration.zero_bits_loop_flag = false; enum_type->zero_bits = !type_has_bits(tag_int_type); enum_type->data.enumeration.zero_bits_known = true; } static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { assert(struct_type->id == TypeTableEntryIdStruct); if (struct_type->data.structure.zero_bits_known) return; if (struct_type->data.structure.zero_bits_loop_flag) { // If we get here it's due to recursion. This is a design flaw in the compiler, // we should be able to still figure out alignment, but here we give up and say that // the alignment is pointer width, then assert that the first field is within that // alignment struct_type->data.structure.zero_bits_known = true; struct_type->data.structure.zero_bits_loop_flag = false; if (struct_type->data.structure.abi_alignment == 0) { if (struct_type->data.structure.layout == ContainerLayoutPacked) { struct_type->data.structure.abi_alignment = 1; } else { struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0)); } } return; } struct_type->data.structure.zero_bits_loop_flag = true; AstNode *decl_node = struct_type->data.structure.decl_node; assert(decl_node->type == NodeTypeContainerDecl); assert(struct_type->di_type); assert(!struct_type->data.structure.fields); size_t field_count = decl_node->data.container_decl.fields.length; struct_type->data.structure.src_field_count = (uint32_t)field_count; struct_type->data.structure.fields = allocate(field_count); struct_type->data.structure.fields_by_name.init(field_count); Scope *scope = &struct_type->data.structure.decls_scope->base; size_t gen_field_index = 0; for (size_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; type_struct_field->name = field_node->data.struct_field.name; type_struct_field->decl_node = field_node; if (field_node->data.struct_field.type == nullptr) { add_node_error(g, field_node, buf_sprintf("struct field missing type")); struct_type->data.structure.is_invalid = true; continue; } auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field); if (field_entry != nullptr) { ErrorMsg *msg = add_node_error(g, field_node, buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name))); add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); struct_type->data.structure.is_invalid = true; continue; } TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); type_struct_field->type_entry = field_type; type_struct_field->src_index = i; type_struct_field->gen_index = SIZE_MAX; if (field_node->data.struct_field.value != nullptr) { add_node_error(g, field_node->data.struct_field.value, buf_sprintf("enums, not structs, support field assignment")); } type_ensure_zero_bits_known(g, field_type); if (type_is_invalid(field_type)) { struct_type->data.structure.is_invalid = true; continue; } if (type_requires_comptime(field_type)) { struct_type->data.structure.requires_comptime = true; } if (!type_has_bits(field_type)) continue; if (gen_field_index == 0) { if (struct_type->data.structure.layout == ContainerLayoutPacked) { struct_type->data.structure.abi_alignment = 1; } else if (struct_type->data.structure.abi_alignment == 0) { // Alignment of structs is the alignment of the first field, for now. // TODO change this when we re-order struct fields (issue #168) struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type); assert(struct_type->data.structure.abi_alignment != 0); } else { // due to a design flaw in the compiler we assumed that alignment was // pointer width, so we assert that this wasn't violated. if (get_abi_alignment(g, field_type) > struct_type->data.structure.abi_alignment) { zig_panic("compiler design flaw: incorrect alignment assumption"); } } } type_struct_field->gen_index = gen_field_index; gen_field_index += 1; } struct_type->data.structure.zero_bits_loop_flag = false; struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index; struct_type->zero_bits = (gen_field_index == 0); struct_type->data.structure.zero_bits_known = true; } static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { assert(union_type->id == TypeTableEntryIdUnion); if (union_type->data.unionation.zero_bits_known) return; if (type_is_invalid(union_type)) return; if (union_type->data.unionation.zero_bits_loop_flag) { // If we get here it's due to recursion. From this we conclude that the struct is // not zero bits, and if abi_alignment == 0 we further conclude that the first field // is a pointer to this very struct, or a function pointer with parameters that // reference such a type. union_type->data.unionation.zero_bits_known = true; union_type->data.unionation.zero_bits_loop_flag = false; if (union_type->data.unionation.abi_alignment == 0) { if (union_type->data.unionation.layout == ContainerLayoutPacked) { union_type->data.unionation.abi_alignment = 1; } else { union_type->data.unionation.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0)); } } return; } union_type->data.unionation.zero_bits_loop_flag = true; AstNode *decl_node = union_type->data.unionation.decl_node; assert(decl_node->type == NodeTypeContainerDecl); assert(union_type->di_type); assert(!union_type->data.unionation.fields); uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; if (field_count == 0) { add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields")); union_type->data.unionation.src_field_count = field_count; union_type->data.unionation.fields = nullptr; union_type->data.unionation.is_invalid = true; union_type->data.unionation.zero_bits_loop_flag = false; union_type->data.unionation.zero_bits_known = true; return; } union_type->data.unionation.src_field_count = field_count; union_type->data.unionation.fields = allocate(field_count); union_type->data.unionation.fields_by_name.init(field_count); uint32_t biggest_align_bytes = 0; Scope *scope = &union_type->data.unionation.decls_scope->base; HashMap occupied_tag_values = {}; AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr; union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum || enum_type_node != nullptr; bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); TypeTableEntry *tag_type; bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); bool *covered_enum_fields; ZigLLVMDIEnumerator **di_enumerators; uint32_t abi_alignment_so_far; if (create_enum_type) { occupied_tag_values.init(field_count); di_enumerators = allocate(field_count); TypeTableEntry *tag_int_type; if (enum_type_node != nullptr) { tag_int_type = analyze_type_expr(g, scope, enum_type_node); if (type_is_invalid(tag_int_type)) { union_type->data.unionation.is_invalid = true; return; } if (tag_int_type->id != TypeTableEntryIdInt) { add_node_error(g, enum_type_node, buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name))); union_type->data.unionation.is_invalid = true; return; } } else { tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); } abi_alignment_so_far = get_abi_alignment(g, tag_int_type); tag_type = new_type_table_entry(TypeTableEntryIdEnum); buf_resize(&tag_type->name, 0); buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name)); tag_type->is_copyable = true; tag_type->type_ref = tag_int_type->type_ref; tag_type->zero_bits = tag_int_type->zero_bits; tag_type->data.enumeration.tag_int_type = tag_int_type; tag_type->data.enumeration.zero_bits_known = true; tag_type->data.enumeration.decl_node = decl_node; tag_type->data.enumeration.layout = ContainerLayoutAuto; tag_type->data.enumeration.src_field_count = field_count; tag_type->data.enumeration.fields = allocate(field_count); tag_type->data.enumeration.fields_by_name.init(field_count); tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope; tag_type->data.enumeration.complete = true; } else if (enum_type_node != nullptr) { TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); if (type_is_invalid(enum_type)) { union_type->data.unionation.is_invalid = true; return; } if (enum_type->id != TypeTableEntryIdEnum) { union_type->data.unionation.is_invalid = true; add_node_error(g, enum_type_node, buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); return; } tag_type = enum_type; abi_alignment_so_far = get_abi_alignment(g, enum_type); // this populates src_field_count covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); } else { tag_type = nullptr; abi_alignment_so_far = 0; } union_type->data.unionation.tag_type = tag_type; uint32_t gen_field_index = 0; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); Buf *field_name = field_node->data.struct_field.name; TypeUnionField *union_field = &union_type->data.unionation.fields[i]; union_field->name = field_node->data.struct_field.name; union_field->decl_node = field_node; auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field); if (field_entry != nullptr) { ErrorMsg *msg = add_node_error(g, field_node, buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name))); add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); union_type->data.unionation.is_invalid = true; continue; } TypeTableEntry *field_type; if (field_node->data.struct_field.type == nullptr) { if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { field_type = g->builtin_types.entry_void; } else { add_node_error(g, field_node, buf_sprintf("union field missing type")); union_type->data.unionation.is_invalid = true; continue; } } else { field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); type_ensure_zero_bits_known(g, field_type); if (type_is_invalid(field_type)) { union_type->data.unionation.is_invalid = true; continue; } } union_field->type_entry = field_type; if (type_requires_comptime(field_type)) { union_type->data.unionation.requires_comptime = true; } if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, buf_sprintf("non-enum union field assignment")); add_error_note(g, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); } if (create_enum_type) { di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i); union_field->enum_field = &tag_type->data.enumeration.fields[i]; union_field->enum_field->name = field_name; union_field->enum_field->decl_index = i; union_field->enum_field->decl_node = field_node; auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field); assert(prev_entry == nullptr); // caught by union de-duplicator above AstNode *tag_value = field_node->data.struct_field.value; // In this first pass we resolve explicit tag values. // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); if (result_inst->value.type->id == TypeTableEntryIdInvalid) { union_type->data.unionation.is_invalid = true; continue; } assert(result_inst->value.special != ConstValSpecialRuntime); assert(result_inst->value.type->id == TypeTableEntryIdInt); auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); if (entry == nullptr) { bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); } else { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); add_error_note(g, msg, entry->value, buf_sprintf("other occurrence here")); union_type->data.unionation.is_invalid = true; continue; } } } else if (enum_type_node != nullptr) { union_field->enum_field = find_enum_type_field(tag_type, field_name); if (union_field->enum_field == nullptr) { ErrorMsg *msg = add_node_error(g, field_node, buf_sprintf("enum field not found: '%s'", buf_ptr(field_name))); add_error_note(g, msg, tag_type->data.enumeration.decl_node, buf_sprintf("enum declared here")); union_type->data.unionation.is_invalid = true; continue; } covered_enum_fields[union_field->enum_field->decl_index] = true; } else { union_field->enum_field = allocate(1); union_field->enum_field->name = field_name; union_field->enum_field->decl_index = i; bigint_init_unsigned(&union_field->enum_field->value, i); } assert(union_field->enum_field != nullptr); if (!type_has_bits(field_type)) continue; union_field->gen_index = gen_field_index; gen_field_index += 1; uint32_t field_align_bytes = get_abi_alignment(g, field_type); if (field_align_bytes > biggest_align_bytes) { biggest_align_bytes = field_align_bytes; if (biggest_align_bytes > abi_alignment_so_far) { abi_alignment_so_far = biggest_align_bytes; } } } union_type->data.unionation.abi_alignment = abi_alignment_so_far; if (union_type->data.unionation.is_invalid) return; bool src_have_tag = decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr; if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) { const char *qual_str; switch (union_type->data.unionation.layout) { case ContainerLayoutAuto: zig_unreachable(); case ContainerLayoutPacked: qual_str = "packed"; break; case ContainerLayoutExtern: qual_str = "extern"; break; } AstNode *source_node = (decl_node->data.container_decl.init_arg_expr != nullptr) ? decl_node->data.container_decl.init_arg_expr : decl_node; add_node_error(g, source_node, buf_sprintf("%s union does not support enum tag type", qual_str)); union_type->data.unionation.is_invalid = true; return; } if (create_enum_type) { // Now iterate again and populate the unspecified tag values uint32_t next_maybe_unoccupied_index = 0; for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); TypeUnionField *union_field = &union_type->data.unionation.fields[field_i]; AstNode *tag_value = field_node->data.struct_field.value; if (tag_value == nullptr) { if (occupied_tag_values.size() == 0) { bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index); next_maybe_unoccupied_index += 1; } else { BigInt proposed_value; for (;;) { bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); next_maybe_unoccupied_index += 1; auto entry = occupied_tag_values.put_unique(proposed_value, field_node); if (entry != nullptr) { continue; } break; } bigint_init_bigint(&union_field->enum_field->value, &proposed_value); } } } } else if (enum_type_node != nullptr) { for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; if (!covered_enum_fields[i]) { AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); ErrorMsg *msg = add_node_error(g, decl_node, buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name))); add_error_note(g, msg, field_node, buf_sprintf("declared here")); union_type->data.unionation.is_invalid = true; } } } if (create_enum_type) { ImportTableEntry *import = get_scope_import(scope); uint64_t tag_debug_size_in_bits = tag_type->zero_bits ? 0 : 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); uint64_t tag_debug_align_in_bits = tag_type->zero_bits ? 0 : 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); // TODO get a more accurate debug scope ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&tag_type->name), import->di_file, (unsigned)(decl_node->line + 1), tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, tag_type->di_type, ""); tag_type->di_type = tag_di_type; } union_type->data.unionation.zero_bits_loop_flag = false; union_type->data.unionation.gen_field_count = gen_field_index; union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !src_have_tag)); union_type->data.unionation.zero_bits_known = true; } static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) { if (!scope) return; if (scope->id == ScopeIdDecls) { get_fully_qualified_decl_name_internal(buf, scope->parent, sep); ScopeDecls *scope_decls = (ScopeDecls *)scope; if (scope_decls->container_type) { buf_append_buf(buf, &scope_decls->container_type->name); buf_append_char(buf, sep); } return; } get_fully_qualified_decl_name_internal(buf, scope->parent, sep); } static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, uint8_t sep) { buf_resize(buf, 0); get_fully_qualified_decl_name_internal(buf, tld->parent_scope, sep); buf_append_buf(buf, tld->name); } FnTableEntry *create_fn_raw(FnInline inline_value) { FnTableEntry *fn_entry = allocate(1); fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc; fn_entry->analyzed_executable.backward_branch_quota = default_backward_branch_quota; fn_entry->analyzed_executable.fn_entry = fn_entry; fn_entry->ir_executable.fn_entry = fn_entry; fn_entry->fn_inline = inline_value; return fn_entry; } FnTableEntry *create_fn(AstNode *proto_node) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; FnInline inline_value = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto; FnTableEntry *fn_entry = create_fn_raw(inline_value); fn_entry->proto_node = proto_node; fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr : proto_node->data.fn_proto.fn_def_node->data.fn_def.body; return fn_entry; } static bool scope_is_root_decls(Scope *scope) { while (scope) { if (scope->id == ScopeIdDecls) { ScopeDecls *scope_decls = (ScopeDecls *)scope; return (scope_decls->container_type == nullptr); } scope = scope->parent; } zig_unreachable(); } static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) { add_node_error(g, proto_node, buf_sprintf("expected 'fn([]const u8, ?&builtin.StackTrace) unreachable', found '%s'", buf_ptr(&fn_type->name))); } static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { AstNode *proto_node = panic_fn->proto_node; assert(proto_node->type == NodeTypeFnProto); TypeTableEntry *fn_type = panic_fn->type_entry; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (fn_type_id->param_count != 2) { return wrong_panic_prototype(g, proto_node, fn_type); } TypeTableEntry *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *const_u8_slice = get_slice_type(g, const_u8_ptr); if (fn_type_id->param_info[0].type != const_u8_slice) { return wrong_panic_prototype(g, proto_node, fn_type); } TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) { return wrong_panic_prototype(g, proto_node, fn_type); } TypeTableEntry *actual_return_type = fn_type_id->return_type; if (actual_return_type != g->builtin_types.entry_unreachable) { return wrong_panic_prototype(g, proto_node, fn_type); } } TypeTableEntry *get_test_fn_type(CodeGen *g) { if (g->test_fn_type) return g->test_fn_type; FnTypeId fn_type_id = {0}; fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set, g->builtin_types.entry_void); g->test_fn_type = get_fn_type(g, &fn_type_id); return g->test_fn_type; } void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc) { if (ccc) { if (buf_eql_str(symbol_name, "main") && g->libc_link_lib != nullptr) { g->have_c_main = true; g->windows_subsystem_windows = false; g->windows_subsystem_console = true; } else if (buf_eql_str(symbol_name, "WinMain") && g->zig_target.os == OsWindows) { g->have_winmain = true; g->windows_subsystem_windows = true; g->windows_subsystem_console = false; } else if (buf_eql_str(symbol_name, "WinMainCRTStartup") && g->zig_target.os == OsWindows) { g->have_winmain_crt_startup = true; } else if (buf_eql_str(symbol_name, "DllMainCRTStartup") && g->zig_target.os == OsWindows) { g->have_dllmain_crt_startup = true; } } FnExport *fn_export = fn_table_entry->export_list.add_one(); memset(fn_export, 0, sizeof(FnExport)); buf_init_from_buf(&fn_export->name, symbol_name); fn_export->linkage = linkage; } static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { ImportTableEntry *import = tld_fn->base.import; AstNode *source_node = tld_fn->base.source_node; if (source_node->type == NodeTypeFnProto) { AstNodeFnProto *fn_proto = &source_node->data.fn_proto; AstNode *fn_def_node = fn_proto->fn_def_node; FnTableEntry *fn_table_entry = create_fn(source_node); get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_'); if (fn_proto->is_export) { bool ccc = (fn_proto->cc == CallingConventionUnspecified || fn_proto->cc == CallingConventionC); add_fn_export(g, fn_table_entry, &fn_table_entry->symbol_name, GlobalLinkageIdStrong, ccc); } tld_fn->fn_entry = fn_table_entry; if (fn_table_entry->body_node) { fn_table_entry->fndef_scope = create_fndef_scope( fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry); for (size_t i = 0; i < fn_proto->params.length; i += 1) { AstNode *param_node = fn_proto->params.at(i); assert(param_node->type == NodeTypeParamDecl); if (param_node->data.param_decl.name == nullptr) { add_node_error(g, param_node, buf_sprintf("missing parameter name")); } } } else { g->external_prototypes.put_unique(tld_fn->base.name, &tld_fn->base); } Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry); if (fn_proto->section_expr != nullptr) { if (fn_table_entry->body_node == nullptr) { add_node_error(g, fn_proto->section_expr, buf_sprintf("cannot set section of external function '%s'", buf_ptr(&fn_table_entry->symbol_name))); } else { analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name); } } if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) { tld_fn->base.resolution = TldResolutionInvalid; return; } if (!fn_table_entry->type_entry->data.fn.is_generic) { if (fn_def_node) g->fn_defs.append(fn_table_entry); if (scope_is_root_decls(tld_fn->base.parent_scope) && (import == g->root_import || import->package == g->panic_package)) { if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { g->main_fn = fn_table_entry; } else if ((import->package == g->panic_package || g->have_pub_panic) && buf_eql_str(&fn_table_entry->symbol_name, "panic")) { g->panic_fn = fn_table_entry; typecheck_panic_fn(g, fn_table_entry); } } } } else if (source_node->type == NodeTypeTestDecl) { FnTableEntry *fn_table_entry = create_fn_raw(FnInlineAuto); get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_'); tld_fn->fn_entry = fn_table_entry; fn_table_entry->proto_node = source_node; fn_table_entry->fndef_scope = create_fndef_scope(source_node, tld_fn->base.parent_scope, fn_table_entry); fn_table_entry->type_entry = get_test_fn_type(g); fn_table_entry->body_node = source_node->data.test_decl.body; fn_table_entry->is_test = true; g->fn_defs.append(fn_table_entry); g->test_fns.append(fn_table_entry); } else { zig_unreachable(); } } static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) { assert(tld_comptime->base.source_node->type == NodeTypeCompTime); AstNode *expr_node = tld_comptime->base.source_node->data.comptime_expr.expr; analyze_const_value(g, tld_comptime->base.parent_scope, expr_node, g->builtin_types.entry_void, nullptr); } static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { bool is_export = false; if (tld->id == TldIdVar) { assert(tld->source_node->type == NodeTypeVariableDeclaration); is_export = tld->source_node->data.variable_declaration.is_export; } else if (tld->id == TldIdFn) { assert(tld->source_node->type == NodeTypeFnProto); is_export = tld->source_node->data.fn_proto.is_export; } if (is_export) { g->resolve_queue.append(tld); auto entry = g->exported_symbol_names.put_unique(tld->name, tld->source_node); if (entry) { AstNode *other_source_node = entry->value; ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name))); add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here")); } } { auto entry = decls_scope->decl_table.put_unique(tld->name, tld); if (entry) { Tld *other_tld = entry->value; ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("redefinition of '%s'", buf_ptr(tld->name))); add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition is here")); return; } } { auto entry = g->primitive_type_table.maybe_get(tld->name); if (entry) { TypeTableEntry *type = entry->value; add_node_error(g, tld->source_node, buf_sprintf("declaration shadows type '%s'", buf_ptr(&type->name))); } } } static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) { assert(node->type == NodeTypeTestDecl); if (!g->is_test_build) return; ImportTableEntry *import = get_scope_import(&decls_scope->base); if (import->package != g->root_package) return; Buf *decl_name_buf = node->data.test_decl.name; Buf *test_name = g->test_name_prefix ? buf_sprintf("%s%s", buf_ptr(g->test_name_prefix), buf_ptr(decl_name_buf)) : decl_name_buf; if (g->test_filter != nullptr && strstr(buf_ptr(test_name), buf_ptr(g->test_filter)) == nullptr) { return; } TldFn *tld_fn = allocate(1); init_tld(&tld_fn->base, TldIdFn, test_name, VisibModPrivate, node, &decls_scope->base); g->resolve_queue.append(&tld_fn->base); } static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) { assert(node->type == NodeTypeCompTime); TldCompTime *tld_comptime = allocate(1); init_tld(&tld_comptime->base, TldIdCompTime, nullptr, VisibModPrivate, node, &decls_scope->base); g->resolve_queue.append(&tld_comptime->base); } void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope) { tld->id = id; tld->name = name; tld->visib_mod = visib_mod; tld->source_node = source_node; tld->import = source_node ? source_node->owner : nullptr; tld->parent_scope = parent_scope; } void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { Tld *tld = g->compile_var_import->decls_scope->decl_table.get(name); resolve_top_level_decl(g, tld, false, tld->source_node); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; tld_var->var->value = value; tld_var->var->align_bytes = get_abi_alignment(g, value->type); } void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { switch (node->type) { case NodeTypeRoot: for (size_t i = 0; i < node->data.root.top_level_decls.length; i += 1) { AstNode *child = node->data.root.top_level_decls.at(i); scan_decls(g, decls_scope, child); } break; case NodeTypeFnDef: scan_decls(g, decls_scope, node->data.fn_def.fn_proto); break; case NodeTypeVariableDeclaration: { Buf *name = node->data.variable_declaration.symbol; VisibMod visib_mod = node->data.variable_declaration.visib_mod; TldVar *tld_var = allocate(1); init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base); tld_var->extern_lib_name = node->data.variable_declaration.lib_name; add_top_level_decl(g, decls_scope, &tld_var->base); break; } case NodeTypeFnProto: { // if the name is missing, we immediately announce an error Buf *fn_name = node->data.fn_proto.name; if (fn_name == nullptr) { add_node_error(g, node, buf_sprintf("missing function name")); break; } VisibMod visib_mod = node->data.fn_proto.visib_mod; TldFn *tld_fn = allocate(1); init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base); tld_fn->extern_lib_name = node->data.fn_proto.lib_name; add_top_level_decl(g, decls_scope, &tld_fn->base); break; } case NodeTypeUse: { g->use_queue.append(node); ImportTableEntry *import = get_scope_import(&decls_scope->base); import->use_decls.append(node); break; } case NodeTypeTestDecl: preview_test_decl(g, node, decls_scope); break; case NodeTypeCompTime: preview_comptime_decl(g, node, decls_scope); break; case NodeTypeContainerDecl: case NodeTypeParamDecl: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeBlock: case NodeTypeGroupedExpr: case NodeTypeBinOpExpr: case NodeTypeUnwrapErrorExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeFloatLiteral: case NodeTypeIntLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeThisLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypePointerType: case NodeTypeIfBoolExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeUnreachable: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypePtrDeref: case NodeTypeUnwrapOptional: case NodeTypeStructField: case NodeTypeContainerInitExpr: case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeIfErrorExpr: case NodeTypeTestExpr: case NodeTypeErrorSetDecl: case NodeTypeCancel: case NodeTypeResume: case NodeTypeAwaitExpr: case NodeTypeSuspend: case NodeTypePromiseType: zig_unreachable(); } } static void resolve_decl_container(CodeGen *g, TldContainer *tld_container) { TypeTableEntry *type_entry = tld_container->type_entry; assert(type_entry); switch (type_entry->id) { case TypeTableEntryIdStruct: resolve_struct_type(g, tld_container->type_entry); return; case TypeTableEntryIdEnum: resolve_enum_type(g, tld_container->type_entry); return; case TypeTableEntryIdUnion: resolve_union_type(g, tld_container->type_entry); return; default: zig_unreachable(); } } TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: case TypeTableEntryIdPromise: return type_entry; } zig_unreachable(); } // Set name to nullptr to make the variable anonymous (not visible to programmer). // TODO merge with definition of add_local_var in ir.cpp VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, bool is_const, ConstExprValue *value, Tld *src_tld) { assert(value); VariableTableEntry *variable_entry = allocate(1); variable_entry->value = value; variable_entry->parent_scope = parent_scope; variable_entry->shadowable = false; variable_entry->mem_slot_index = SIZE_MAX; variable_entry->src_arg_index = SIZE_MAX; variable_entry->align_bytes = get_abi_alignment(g, value->type); assert(name); buf_init_from_buf(&variable_entry->name, name); if (value->type->id != TypeTableEntryIdInvalid) { VariableTableEntry *existing_var = find_variable(g, parent_scope, name); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->value->type = g->builtin_types.entry_invalid; } else { auto primitive_table_entry = g->primitive_type_table.maybe_get(name); if (primitive_table_entry) { TypeTableEntry *type = primitive_table_entry->value; add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->value->type = g->builtin_types.entry_invalid; } else { Scope *search_scope = nullptr; if (src_tld == nullptr) { search_scope = parent_scope; } else if (src_tld->parent_scope != nullptr && src_tld->parent_scope->parent != nullptr) { search_scope = src_tld->parent_scope->parent; } if (search_scope != nullptr) { Tld *tld = find_decl(g, search_scope, name); if (tld != nullptr) { ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here")); variable_entry->value->type = g->builtin_types.entry_invalid; } } } } } Scope *child_scope; if (source_node && source_node->type == NodeTypeParamDecl) { child_scope = create_var_scope(source_node, parent_scope, variable_entry); } else { // it's already in the decls table child_scope = parent_scope; } variable_entry->src_is_const = is_const; variable_entry->gen_is_const = is_const; variable_entry->decl_node = source_node; variable_entry->child_scope = child_scope; return variable_entry; } static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { AstNode *source_node = tld_var->base.source_node; AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration; bool is_const = var_decl->is_const; bool is_extern = var_decl->is_extern; bool is_export = var_decl->is_export; TypeTableEntry *explicit_type = nullptr; if (var_decl->type) { TypeTableEntry *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type); explicit_type = validate_var_type(g, var_decl->type, proposed_type); } assert(!is_export || !is_extern); VarLinkage linkage; if (is_export) { linkage = VarLinkageExport; } else if (is_extern) { linkage = VarLinkageExternal; } else { linkage = VarLinkageInternal; } IrInstruction *init_value = nullptr; // TODO more validation for types that can't be used for export/extern variables TypeTableEntry *implicit_type = nullptr; if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) { implicit_type = explicit_type; } else if (var_decl->expr) { init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol); assert(init_value); implicit_type = init_value->value.type; if (implicit_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); implicit_type = g->builtin_types.entry_invalid; } else if ((!is_const || linkage == VarLinkageExternal) && (implicit_type->id == TypeTableEntryIdComptimeFloat || implicit_type->id == TypeTableEntryIdComptimeInt)) { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; } else if (implicit_type->id == TypeTableEntryIdNull) { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) { add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant")); implicit_type = g->builtin_types.entry_invalid; } assert(implicit_type->id == TypeTableEntryIdInvalid || init_value->value.special != ConstValSpecialRuntime); } else if (linkage != VarLinkageExternal) { add_node_error(g, source_node, buf_sprintf("variables must be initialized")); implicit_type = g->builtin_types.entry_invalid; } TypeTableEntry *type = explicit_type ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type); tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, is_const, init_val, &tld_var->base); tld_var->var->linkage = linkage; if (implicit_type != nullptr && type_is_invalid(implicit_type)) { tld_var->var->value->type = g->builtin_types.entry_invalid; } if (var_decl->align_expr != nullptr) { if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) { tld_var->var->value->type = g->builtin_types.entry_invalid; } } if (var_decl->section_expr != nullptr) { if (var_decl->is_extern) { add_node_error(g, var_decl->section_expr, buf_sprintf("cannot set section of external variable '%s'", buf_ptr(var_decl->symbol))); } else if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->section_name)) { tld_var->section_name = nullptr; } } g->global_vars.append(tld_var); } void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node) { if (tld->resolution != TldResolutionUnresolved) return; if (tld->dep_loop_flag) { add_node_error(g, tld->source_node, buf_sprintf("'%s' depends on itself", buf_ptr(tld->name))); tld->resolution = TldResolutionInvalid; return; } tld->dep_loop_flag = true; g->tld_ref_source_node_stack.append(source_node); switch (tld->id) { case TldIdVar: { TldVar *tld_var = (TldVar *)tld; resolve_decl_var(g, tld_var); break; } case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; resolve_decl_fn(g, tld_fn); break; } case TldIdContainer: { TldContainer *tld_container = (TldContainer *)tld; resolve_decl_container(g, tld_container); break; } case TldIdCompTime: { TldCompTime *tld_comptime = (TldCompTime *)tld; resolve_decl_comptime(g, tld_comptime); break; } } tld->resolution = TldResolutionOk; tld->dep_loop_flag = false; g->tld_ref_source_node_stack.pop(); } Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) { // we must resolve all the use decls ImportTableEntry *import = get_scope_import(scope); for (size_t i = 0; i < import->use_decls.length; i += 1) { AstNode *use_decl_node = import->use_decls.at(i); if (use_decl_node->data.use.resolution == TldResolutionUnresolved) { preview_use_decl(g, use_decl_node); resolve_use_decl(g, use_decl_node); } } while (scope) { if (scope->id == ScopeIdDecls) { ScopeDecls *decls_scope = (ScopeDecls *)scope; auto entry = decls_scope->decl_table.maybe_get(name); if (entry) return entry->value; } scope = scope->parent; } return nullptr; } VariableTableEntry *find_variable(CodeGen *g, Scope *scope, Buf *name) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; if (buf_eql_buf(name, &var_scope->var->name)) return var_scope->var; } else if (scope->id == ScopeIdDecls) { ScopeDecls *decls_scope = (ScopeDecls *)scope; auto entry = decls_scope->decl_table.maybe_get(name); if (entry) { Tld *tld = entry->value; if (tld->id == TldIdVar) { TldVar *tld_var = (TldVar *)tld; if (tld_var->var) return tld_var->var; } } } scope = scope->parent; } return nullptr; } FnTableEntry *scope_fn_entry(Scope *scope) { while (scope) { if (scope->id == ScopeIdFnDef) { ScopeFnDef *fn_scope = (ScopeFnDef *)scope; return fn_scope->fn_entry; } scope = scope->parent; } return nullptr; } FnTableEntry *scope_get_fn_if_root(Scope *scope) { assert(scope); scope = scope->parent; while (scope) { switch (scope->id) { case ScopeIdBlock: return nullptr; case ScopeIdDecls: case ScopeIdDefer: case ScopeIdDeferExpr: case ScopeIdVarDecl: case ScopeIdCImport: case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdCoroPrelude: scope = scope->parent; continue; case ScopeIdFnDef: ScopeFnDef *fn_scope = (ScopeFnDef *)scope; return fn_scope->fn_entry; } zig_unreachable(); } return nullptr; } TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) { assert(enum_type->id == TypeTableEntryIdEnum); if (enum_type->data.enumeration.src_field_count == 0) return nullptr; auto entry = enum_type->data.enumeration.fields_by_name.maybe_get(name); if (entry == nullptr) return nullptr; return entry->value; } TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->id == TypeTableEntryIdStruct); assert(type_entry->data.structure.complete); if (type_entry->data.structure.src_field_count == 0) return nullptr; auto entry = type_entry->data.structure.fields_by_name.maybe_get(name); if (entry == nullptr) return nullptr; return entry->value; } TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->id == TypeTableEntryIdUnion); assert(type_entry->data.unionation.zero_bits_known); if (type_entry->data.unionation.src_field_count == 0) return nullptr; auto entry = type_entry->data.unionation.fields_by_name.maybe_get(name); if (entry == nullptr) return nullptr; return entry->value; } TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { assert(type_entry->id == TypeTableEntryIdUnion); assert(type_entry->data.unionation.zero_bits_known); for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { TypeUnionField *field = &type_entry->data.unionation.fields[i]; if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) { return field; } } return nullptr; } TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag) { for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *field = &enum_type->data.enumeration.fields[i]; if (bigint_cmp(&field->value, tag) == CmpEQ) { return field; } } return nullptr; } static bool is_container(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdStruct: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: return true; case TypeTableEntryIdPointer: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: return false; } zig_unreachable(); } bool is_ref(TypeTableEntry *type_entry) { return type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle; } bool is_array_ref(TypeTableEntry *type_entry) { TypeTableEntry *array = is_ref(type_entry) ? type_entry->data.pointer.child_type : type_entry; return array->id == TypeTableEntryIdArray; } bool is_container_ref(TypeTableEntry *type_entry) { return is_ref(type_entry) ? is_container(type_entry->data.pointer.child_type) : is_container(type_entry); } TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { assert(is_container_ref(type_entry)); return is_ref(type_entry) ? type_entry->data.pointer.child_type : type_entry; } void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdStruct: resolve_struct_type(g, type_entry); break; case TypeTableEntryIdEnum: resolve_enum_type(g, type_entry); break; case TypeTableEntryIdUnion: resolve_union_type(g, type_entry); break; case TypeTableEntryIdPointer: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: zig_unreachable(); } } TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return type; if (type->id == TypeTableEntryIdFn) return type; if (type->id == TypeTableEntryIdPromise) return type; if (type->id == TypeTableEntryIdOptional) { if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type; } return nullptr; } bool type_is_codegen_pointer(TypeTableEntry *type) { return get_codegen_ptr_type(type) == type; } uint32_t get_ptr_align(TypeTableEntry *type) { TypeTableEntry *ptr_type = get_codegen_ptr_type(type); if (ptr_type->id == TypeTableEntryIdPointer) { return ptr_type->data.pointer.alignment; } else if (ptr_type->id == TypeTableEntryIdFn) { return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment; } else if (ptr_type->id == TypeTableEntryIdPromise) { return 1; } else { zig_unreachable(); } } bool get_ptr_const(TypeTableEntry *type) { TypeTableEntry *ptr_type = get_codegen_ptr_type(type); if (ptr_type->id == TypeTableEntryIdPointer) { return ptr_type->data.pointer.is_const; } else if (ptr_type->id == TypeTableEntryIdFn) { return true; } else if (ptr_type->id == TypeTableEntryIdPromise) { return true; } else { zig_unreachable(); } } AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) { if (fn_entry->param_source_nodes) return fn_entry->param_source_nodes[index]; else if (fn_entry->proto_node) return fn_entry->proto_node->data.fn_proto.params.at(index); else return nullptr; } static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) { TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); Buf *param_name; bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args; if (param_decl_node && !is_var_args) { param_name = param_decl_node->data.param_decl.name; } else { param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i); } if (param_name == nullptr) { continue; } TypeTableEntry *param_type = param_info->type; bool is_noalias = param_info->is_noalias; if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); } VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, param_name, true, create_const_runtime(param_type), nullptr); var->src_arg_index = i; fn_table_entry->child_scope = var->child_scope; var->shadowable = var->shadowable || is_var_args; if (type_has_bits(param_type)) { fn_table_entry->variable_list.append(var); } if (fn_type->data.fn.gen_param_info) { var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; } if (arg_vars) { arg_vars[i] = var; } } } bool resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) { FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; if (infer_fn != nullptr) { if (infer_fn->anal_state == FnAnalStateInvalid) { return false; } else if (infer_fn->anal_state == FnAnalStateReady) { analyze_fn_body(g, infer_fn); if (err_set_type->data.error_set.infer_fn != nullptr) { assert(g->errors.length != 0); return false; } } else { add_node_error(g, source_node, buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); return false; } } return true; } void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) { TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; TypeTableEntry *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable, &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node); fn_table_entry->src_implicit_return_type = block_return_type; if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) { assert(g->errors.length > 0); fn_table_entry->anal_state = FnAnalStateInvalid; return; } if (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { TypeTableEntry *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type; if (return_err_set_type->data.error_set.infer_fn != nullptr) { TypeTableEntry *inferred_err_set_type; if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorSet) { inferred_err_set_type = fn_table_entry->src_implicit_return_type; } else if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorUnion) { inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_type; } else { add_node_error(g, return_type_node, buf_sprintf("function with inferred error set must return at least one possible error")); fn_table_entry->anal_state = FnAnalStateInvalid; return; } if (inferred_err_set_type->data.error_set.infer_fn != nullptr) { if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { fn_table_entry->anal_state = FnAnalStateInvalid; return; } } return_err_set_type->data.error_set.infer_fn = nullptr; if (type_is_global_error_set(inferred_err_set_type)) { return_err_set_type->data.error_set.err_count = UINT32_MAX; } else { return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; if (inferred_err_set_type->data.error_set.err_count > 0) { return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; } } } } } if (g->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4); fprintf(stderr, "}\n"); } fn_table_entry->anal_state = FnAnalStateComplete; } static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { assert(fn_table_entry->anal_state != FnAnalStateProbing); if (fn_table_entry->anal_state != FnAnalStateReady) return; fn_table_entry->anal_state = FnAnalStateProbing; AstNode *return_type_node = (fn_table_entry->proto_node != nullptr) ? fn_table_entry->proto_node->data.fn_proto.return_type : fn_table_entry->fndef_scope->base.source_node; assert(fn_table_entry->fndef_scope); if (!fn_table_entry->child_scope) fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base; define_local_param_variables(g, fn_table_entry, nullptr); TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); ir_gen_fn(g, fn_table_entry); if (fn_table_entry->ir_executable.invalid) { fn_table_entry->anal_state = FnAnalStateInvalid; return; } if (g->verbose_ir) { fprintf(stderr, "\n"); ast_render(g, stderr, fn_table_entry->body_node, 4); fprintf(stderr, "\n{ // (IR)\n"); ir_print(g, stderr, &fn_table_entry->ir_executable, 4); fprintf(stderr, "}\n"); } analyze_fn_ir(g, fn_table_entry, return_type_node); } static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) { if (src_use_node->data.use.resolution == TldResolutionUnresolved) { preview_use_decl(g, src_use_node); } IrInstruction *use_target_value = src_use_node->data.use.value; if (use_target_value->value.type->id == TypeTableEntryIdInvalid) { dst_use_node->owner->any_imports_failed = true; return; } dst_use_node->data.use.resolution = TldResolutionOk; ConstExprValue *const_val = &use_target_value->value; assert(const_val->special != ConstValSpecialRuntime); ImportTableEntry *target_import = const_val->data.x_import; assert(target_import); if (target_import->any_imports_failed) { dst_use_node->owner->any_imports_failed = true; } auto it = target_import->decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *target_tld = entry->value; if (target_tld->import != target_import || target_tld->visib_mod == VisibModPrivate) { continue; } Buf *target_tld_name = entry->key; auto existing_entry = dst_use_node->owner->decls_scope->decl_table.put_unique(target_tld_name, target_tld); if (existing_entry) { Tld *existing_decl = existing_entry->value; if (existing_decl != target_tld) { ErrorMsg *msg = add_node_error(g, dst_use_node, buf_sprintf("import of '%s' overrides existing definition", buf_ptr(target_tld_name))); add_error_note(g, msg, existing_decl->source_node, buf_sprintf("previous definition here")); add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here")); } } } for (size_t i = 0; i < target_import->use_decls.length; i += 1) { AstNode *use_decl_node = target_import->use_decls.at(i); if (use_decl_node->data.use.visib_mod != VisibModPrivate) add_symbols_from_import(g, use_decl_node, dst_use_node); } } void resolve_use_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeUse); if (node->data.use.resolution == TldResolutionOk || node->data.use.resolution == TldResolutionInvalid) { return; } add_symbols_from_import(g, node, node); } void preview_use_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeUse); if (node->data.use.resolution == TldResolutionOk || node->data.use.resolution == TldResolutionInvalid) { return; } node->data.use.resolution = TldResolutionResolving; IrInstruction *result = analyze_const_value(g, &node->owner->decls_scope->base, node->data.use.expr, g->builtin_types.entry_namespace, nullptr); if (result->value.type->id == TypeTableEntryIdInvalid) node->owner->any_imports_failed = true; node->data.use.value = result; } ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code) { if (g->verbose_tokenize) { fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(abs_full_path)); fprintf(stderr, "----------------\n"); fprintf(stderr, "%s\n", buf_ptr(source_code)); fprintf(stderr, "\nTokens:\n"); fprintf(stderr, "---------\n"); } Tokenization tokenization = {0}; tokenize(source_code, &tokenization); if (tokenization.err) { ErrorMsg *err = err_msg_create_with_line(abs_full_path, tokenization.err_line, tokenization.err_column, source_code, tokenization.line_offsets, tokenization.err); print_err_msg(err, g->err_color); exit(1); } if (g->verbose_tokenize) { print_tokens(source_code, tokenization.tokens); fprintf(stderr, "\nAST:\n"); fprintf(stderr, "------\n"); } ImportTableEntry *import_entry = allocate(1); import_entry->package = package; import_entry->source_code = source_code; import_entry->line_offsets = tokenization.line_offsets; import_entry->path = abs_full_path; import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color); assert(import_entry->root); if (g->verbose_ast) { ast_print(stderr, import_entry->root, 0); } Buf *src_dirname = buf_alloc(); Buf *src_basename = buf_alloc(); os_path_split(abs_full_path, src_dirname, src_basename); import_entry->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); g->import_table.put(abs_full_path, import_entry); g->import_queue.append(import_entry); import_entry->decls_scope = create_decls_scope(import_entry->root, nullptr, nullptr, import_entry); assert(import_entry->root->type == NodeTypeRoot); for (size_t decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) { AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i); if (top_level_decl->type == NodeTypeFnDef) { AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; assert(proto_node->type == NodeTypeFnProto); Buf *proto_name = proto_node->data.fn_proto.name; bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub); bool ok_cc = (proto_node->data.fn_proto.cc == CallingConventionUnspecified || proto_node->data.fn_proto.cc == CallingConventionCold); if (is_pub && ok_cc) { if (buf_eql_str(proto_name, "main")) { g->have_pub_main = true; g->windows_subsystem_windows = false; g->windows_subsystem_console = true; } else if (buf_eql_str(proto_name, "panic")) { g->have_pub_panic = true; } } } } return import_entry; } void scan_import(CodeGen *g, ImportTableEntry *import) { if (!import->scanned) { import->scanned = true; scan_decls(g, import->decls_scope, import->root); } } void semantic_analyze(CodeGen *g) { for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) { ImportTableEntry *import = g->import_queue.at(g->import_queue_index); scan_import(g, import); } for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) { AstNode *use_decl_node = g->use_queue.at(g->use_queue_index); preview_use_decl(g, use_decl_node); } for (size_t i = 0; i < g->use_queue.length; i += 1) { AstNode *use_decl_node = g->use_queue.at(i); resolve_use_decl(g, use_decl_node); } while (g->resolve_queue_index < g->resolve_queue.length || g->fn_defs_index < g->fn_defs.length) { for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) { Tld *tld = g->resolve_queue.at(g->resolve_queue_index); bool pointer_only = false; AstNode *source_node = nullptr; resolve_top_level_decl(g, tld, pointer_only, source_node); } for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) { FnTableEntry *fn_entry = g->fn_defs.at(g->fn_defs_index); analyze_fn_body(g, fn_entry); } } } TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits) { size_t index; if (size_in_bits == 2) { index = 0; } else if (size_in_bits == 3) { index = 1; } else if (size_in_bits == 4) { index = 2; } else if (size_in_bits == 5) { index = 3; } else if (size_in_bits == 6) { index = 4; } else if (size_in_bits == 7) { index = 5; } else if (size_in_bits == 8) { index = 6; } else if (size_in_bits == 16) { index = 7; } else if (size_in_bits == 29) { index = 8; } else if (size_in_bits == 32) { index = 9; } else if (size_in_bits == 64) { index = 10; } else if (size_in_bits == 128) { index = 11; } else { return nullptr; } return &g->builtin_types.entry_int[is_signed ? 0 : 1][index]; } TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { TypeTableEntry **common_entry = get_int_type_ptr(g, is_signed, size_in_bits); if (common_entry) return *common_entry; TypeId type_id = {}; type_id.id = TypeTableEntryIdInt; type_id.data.integer.is_signed = is_signed; type_id.data.integer.bit_count = size_in_bits; { auto entry = g->type_table.maybe_get(type_id); if (entry) return entry->value; } TypeTableEntry *new_entry = make_int_type(g, is_signed, size_in_bits); g->type_table.put(type_id, new_entry); return new_entry; } TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) { return &g->builtin_types.entry_c_int[c_int_type]; } TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type) { return *get_c_int_type_ptr(g, c_int_type); } bool handle_is_ptr(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdEnum: case TypeTableEntryIdPromise: return false; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: return type_has_bits(type_entry); case TypeTableEntryIdErrorUnion: return type_has_bits(type_entry->data.error_union.payload_type); case TypeTableEntryIdOptional: return type_has_bits(type_entry->data.maybe.child_type) && !type_is_codegen_pointer(type_entry->data.maybe.child_type); case TypeTableEntryIdUnion: assert(type_entry->data.unionation.complete); if (type_entry->data.unionation.gen_field_count == 0) return false; if (!type_has_bits(type_entry)) return false; return true; } zig_unreachable(); } static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { if (g->win_sdk == nullptr) { if (os_find_windows_sdk(&g->win_sdk)) { fprintf(stderr, "unable to determine windows sdk path\n"); exit(1); } } assert(g->win_sdk != nullptr); return g->win_sdk; } Buf *get_linux_libc_lib_path(const char *o_file) { const char *cc_exe = getenv("CC"); cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; ZigList args = {}; args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file))); Termination term; Buf *out_stderr = buf_alloc(); Buf *out_stdout = buf_alloc(); int err; if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { zig_panic("unable to determine libc lib path: executing C compiler: %s", err_str(err)); } if (term.how != TerminationIdClean || term.code != 0) { zig_panic("unable to determine libc lib path: executing C compiler command failed"); } if (buf_ends_with_str(out_stdout, "\n")) { buf_resize(out_stdout, buf_len(out_stdout) - 1); } if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, o_file)) { zig_panic("unable to determine libc lib path: C compiler could not find %s", o_file); } Buf *result = buf_alloc(); os_path_dirname(out_stdout, result); return result; } Buf *get_linux_libc_include_path(void) { const char *cc_exe = getenv("CC"); cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; ZigList args = {}; args.append("-E"); args.append("-Wp,-v"); args.append("-xc"); args.append("/dev/null"); Termination term; Buf *out_stderr = buf_alloc(); Buf *out_stdout = buf_alloc(); int err; if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { zig_panic("unable to determine libc include path: executing C compiler: %s", err_str(err)); } if (term.how != TerminationIdClean || term.code != 0) { zig_panic("unable to determine libc include path: executing C compiler command failed"); } char *prev_newline = buf_ptr(out_stderr); ZigList search_paths = {}; bool found_search_paths = false; for (;;) { char *newline = strchr(prev_newline, '\n'); if (newline == nullptr) { zig_panic("unable to determine libc include path: bad output from C compiler command"); } *newline = 0; if (found_search_paths) { if (strcmp(prev_newline, "End of search list.") == 0) { break; } search_paths.append(prev_newline); } else { if (strcmp(prev_newline, "#include <...> search starts here:") == 0) { found_search_paths = true; } } prev_newline = newline + 1; } if (search_paths.length == 0) { zig_panic("unable to determine libc include path: even C compiler does not know where libc headers are"); } for (size_t i = 0; i < search_paths.length; i += 1) { // search in reverse order const char *search_path = search_paths.items[search_paths.length - i - 1]; // cut off spaces while (*search_path == ' ') { search_path += 1; } Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path); bool exists; if ((err = os_file_exists(stdlib_path, &exists))) { exists = false; } if (exists) { return buf_create_from_str(search_path); } } zig_panic("unable to determine libc include path: stdlib.h not found in C compiler search paths"); } void find_libc_include_path(CodeGen *g) { if (g->libc_include_dir == nullptr) { if (g->zig_target.os == OsWindows) { ZigWindowsSDK *sdk = get_windows_sdk(g); g->libc_include_dir = buf_alloc(); if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) { fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); exit(1); } } else if (g->zig_target.os == OsLinux) { g->libc_include_dir = get_linux_libc_include_path(); } else if (g->zig_target.os == OsMacOSX) { g->libc_include_dir = buf_create_from_str("/usr/include"); } else { // TODO find libc at runtime for other operating systems zig_panic("Unable to determine libc include path."); } } assert(buf_len(g->libc_include_dir) != 0); } void find_libc_lib_path(CodeGen *g) { // later we can handle this better by reporting an error via the normal mechanism if (g->libc_lib_dir == nullptr || (g->zig_target.os == OsWindows && (g->msvc_lib_dir == nullptr || g->kernel32_lib_dir == nullptr))) { if (g->zig_target.os == OsWindows) { ZigWindowsSDK *sdk = get_windows_sdk(g); if (g->msvc_lib_dir == nullptr) { Buf* vc_lib_dir = buf_alloc(); if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); exit(1); } g->msvc_lib_dir = vc_lib_dir; } if (g->libc_lib_dir == nullptr) { Buf* ucrt_lib_path = buf_alloc(); if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir"); exit(1); } g->libc_lib_dir = ucrt_lib_path; } if (g->kernel32_lib_dir == nullptr) { Buf* kern_lib_path = buf_alloc(); if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir"); exit(1); } g->kernel32_lib_dir = kern_lib_path; } } else if (g->zig_target.os == OsLinux) { g->libc_lib_dir = get_linux_libc_lib_path("crt1.o"); } else { zig_panic("Unable to determine libc lib path."); } } else { assert(buf_len(g->libc_lib_dir) != 0); } if (g->libc_static_lib_dir == nullptr) { if ((g->zig_target.os == OsWindows) && (g->msvc_lib_dir != NULL)) { return; } else if (g->zig_target.os == OsLinux) { g->libc_static_lib_dir = get_linux_libc_lib_path("crtbegin.o"); } else { zig_panic("Unable to determine libc static lib path."); } } else { assert(buf_len(g->libc_static_lib_dir) != 0); } } static uint32_t hash_ptr(void *ptr) { return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX); } static uint32_t hash_size(size_t x) { return (uint32_t)(x % UINT32_MAX); } uint32_t fn_table_entry_hash(FnTableEntry* value) { return ptr_hash(value); } bool fn_table_entry_eql(FnTableEntry *a, FnTableEntry *b) { return ptr_eq(a, b); } uint32_t fn_type_id_hash(FnTypeId *id) { uint32_t result = 0; result += ((uint32_t)(id->cc)) * (uint32_t)3349388391; result += id->is_var_args ? (uint32_t)1931444534 : 0; result += hash_ptr(id->return_type); result += hash_ptr(id->async_allocator_type); result += id->alignment * 0xd3b3f3e2; for (size_t i = 0; i < id->param_count; i += 1) { FnTypeParamInfo *info = &id->param_info[i]; result += info->is_noalias ? (uint32_t)892356923 : 0; result += hash_ptr(info->type); } return result; } bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { if (a->cc != b->cc || a->return_type != b->return_type || a->is_var_args != b->is_var_args || a->param_count != b->param_count || a->alignment != b->alignment || a->async_allocator_type != b->async_allocator_type) { return false; } for (size_t i = 0; i < a->param_count; i += 1) { FnTypeParamInfo *a_param_info = &a->param_info[i]; FnTypeParamInfo *b_param_info = &b->param_info[i]; if (a_param_info->type != b_param_info->type || a_param_info->is_noalias != b_param_info->is_noalias) { return false; } } return true; } static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { uint32_t hash_val = 0; switch (const_val->data.x_ptr.mut) { case ConstPtrMutRuntimeVar: hash_val += (uint32_t)3500721036; break; case ConstPtrMutComptimeConst: hash_val += (uint32_t)4214318515; break; case ConstPtrMutComptimeVar: hash_val += (uint32_t)1103195694; break; } switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); case ConstPtrSpecialRef: hash_val += (uint32_t)2478261866; hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee); return hash_val; case ConstPtrSpecialBaseArray: hash_val += (uint32_t)1764906839; hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492; return hash_val; case ConstPtrSpecialBaseStruct: hash_val += (uint32_t)3518317043; hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); return hash_val; case ConstPtrSpecialHardCodedAddr: hash_val += (uint32_t)4048518294; hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); return hash_val; case ConstPtrSpecialDiscard: hash_val += 2010123162; return hash_val; case ConstPtrSpecialFunction: hash_val += (uint32_t)2590901619; hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); return hash_val; } zig_unreachable(); } static uint32_t hash_const_val(ConstExprValue *const_val) { assert(const_val->special == ConstValSpecialStatic); switch (const_val->type->id) { case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdBool: return const_val->data.x_bool ? (uint32_t)127863866 : (uint32_t)215080464; case TypeTableEntryIdMetaType: return hash_ptr(const_val->data.x_type); case TypeTableEntryIdVoid: return (uint32_t)4149439618; case TypeTableEntryIdInt: case TypeTableEntryIdComptimeInt: { uint32_t result = 1331471175; for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) { uint64_t digit = bigint_ptr(&const_val->data.x_bigint)[i]; result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result); } return result; } case TypeTableEntryIdEnum: { uint32_t result = 31643936; for (size_t i = 0; i < const_val->data.x_enum_tag.digit_count; i += 1) { uint64_t digit = bigint_ptr(&const_val->data.x_enum_tag)[i]; result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result); } return result; } case TypeTableEntryIdFloat: switch (const_val->type->data.floating.bit_count) { case 32: { uint32_t result; memcpy(&result, &const_val->data.x_f32, 4); return result ^ 4084870010; } case 64: { uint32_t ints[2]; memcpy(&ints[0], &const_val->data.x_f64, 8); return ints[0] ^ ints[1] ^ 0x22ed43c6; } case 128: { uint32_t ints[4]; memcpy(&ints[0], &const_val->data.x_f128, 16); return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xb5ffef27; } default: zig_unreachable(); } case TypeTableEntryIdComptimeFloat: { float128_t f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); uint32_t ints[4]; memcpy(&ints[0], &f128, 16); return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xed8b3dfb; } case TypeTableEntryIdArgTuple: return (uint32_t)const_val->data.x_arg_tuple.start_index * (uint32_t)281907309 + (uint32_t)const_val->data.x_arg_tuple.end_index * (uint32_t)2290442768; case TypeTableEntryIdFn: assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); case TypeTableEntryIdPointer: return hash_const_val_ptr(const_val); case TypeTableEntryIdPromise: // TODO better hashing algorithm return 223048345; case TypeTableEntryIdUndefined: return 162837799; case TypeTableEntryIdNull: return 844854567; case TypeTableEntryIdArray: // TODO better hashing algorithm return 1166190605; case TypeTableEntryIdStruct: // TODO better hashing algorithm return 1532530855; case TypeTableEntryIdUnion: // TODO better hashing algorithm return 2709806591; case TypeTableEntryIdOptional: if (get_codegen_ptr_type(const_val->type) != nullptr) { return hash_const_val(const_val) * 1992916303; } else { if (const_val->data.x_optional) { return hash_const_val(const_val->data.x_optional) * 1992916303; } else { return 4016830364; } } case TypeTableEntryIdErrorUnion: // TODO better hashing algorithm return 3415065496; case TypeTableEntryIdErrorSet: assert(const_val->data.x_err_set != nullptr); return const_val->data.x_err_set->value ^ 2630160122; case TypeTableEntryIdNamespace: return hash_ptr(const_val->data.x_import); case TypeTableEntryIdBlock: return hash_ptr(const_val->data.x_block); case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdUnreachable: zig_unreachable(); } zig_unreachable(); } uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) { uint32_t result = 0; result += hash_ptr(id->fn_entry); for (size_t i = 0; i < id->param_count; i += 1) { ConstExprValue *generic_param = &id->params[i]; if (generic_param->special != ConstValSpecialRuntime) { result += hash_const_val(generic_param); result += hash_ptr(generic_param->type); } } return result; } bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) { assert(a->fn_entry); if (a->fn_entry != b->fn_entry) return false; if (a->param_count != b->param_count) return false; for (size_t i = 0; i < a->param_count; i += 1) { ConstExprValue *a_val = &a->params[i]; ConstExprValue *b_val = &b->params[i]; if (a_val->type != b_val->type) return false; if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) { assert(a_val->special == ConstValSpecialStatic); assert(b_val->special == ConstValSpecialStatic); if (!const_values_equal(a_val, b_val)) { return false; } } else { assert(a_val->special == ConstValSpecialRuntime && b_val->special == ConstValSpecialRuntime); } } return true; } static bool can_mutate_comptime_var_state(ConstExprValue *value) { assert(value != nullptr); switch (value->type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: case TypeTableEntryIdFn: case TypeTableEntryIdBlock: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: return false; case TypeTableEntryIdPointer: return value->data.x_ptr.mut == ConstPtrMutComptimeVar; case TypeTableEntryIdArray: if (value->type->data.array.len == 0) return false; if (value->data.x_array.special == ConstArraySpecialUndef) return false; for (uint32_t i = 0; i < value->type->data.array.len; i += 1) { if (can_mutate_comptime_var_state(&value->data.x_array.s_none.elements[i])) return true; } return false; case TypeTableEntryIdStruct: for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) { if (can_mutate_comptime_var_state(&value->data.x_struct.fields[i])) return true; } return false; case TypeTableEntryIdOptional: if (get_codegen_ptr_type(value->type) != nullptr) return value->data.x_ptr.mut == ConstPtrMutComptimeVar; if (value->data.x_optional == nullptr) return false; return can_mutate_comptime_var_state(value->data.x_optional); case TypeTableEntryIdErrorUnion: if (value->data.x_err_union.err != nullptr) return false; assert(value->data.x_err_union.payload != nullptr); return can_mutate_comptime_var_state(value->data.x_err_union.payload); case TypeTableEntryIdUnion: return can_mutate_comptime_var_state(value->data.x_union.payload); case TypeTableEntryIdArgTuple: zig_panic("TODO var args at comptime is currently not supported"); } zig_unreachable(); } static bool return_type_is_cacheable(TypeTableEntry *return_type) { switch (return_type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: case TypeTableEntryIdFn: case TypeTableEntryIdBlock: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdPointer: return true; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUnion: return false; case TypeTableEntryIdOptional: return return_type_is_cacheable(return_type->data.maybe.child_type); case TypeTableEntryIdErrorUnion: return return_type_is_cacheable(return_type->data.error_union.payload_type); case TypeTableEntryIdArgTuple: zig_panic("TODO var args at comptime is currently not supported"); } zig_unreachable(); } bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type) { if (!return_type_is_cacheable(return_type)) return false; while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; if (can_mutate_comptime_var_state(var_scope->var->value)) return false; } else if (scope->id == ScopeIdFnDef) { return true; } else { zig_unreachable(); } scope = scope->parent; } zig_unreachable(); } uint32_t fn_eval_hash(Scope* scope) { uint32_t result = 0; while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; result += hash_const_val(var_scope->var->value); } else if (scope->id == ScopeIdFnDef) { ScopeFnDef *fn_scope = (ScopeFnDef *)scope; result += hash_ptr(fn_scope->fn_entry); return result; } else { zig_unreachable(); } scope = scope->parent; } zig_unreachable(); } bool fn_eval_eql(Scope *a, Scope *b) { while (a && b) { if (a->id != b->id) return false; if (a->id == ScopeIdVarDecl) { ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a; ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b; if (a_var_scope->var->value->type != b_var_scope->var->value->type) return false; if (!const_values_equal(a_var_scope->var->value, b_var_scope->var->value)) return false; } else if (a->id == ScopeIdFnDef) { ScopeFnDef *a_fn_scope = (ScopeFnDef *)a; ScopeFnDef *b_fn_scope = (ScopeFnDef *)b; if (a_fn_scope->fn_entry != b_fn_scope->fn_entry) return false; return true; } else { zig_unreachable(); } a = a->parent; b = b->parent; } return false; } bool type_has_bits(TypeTableEntry *type_entry) { assert(type_entry); assert(type_entry->id != TypeTableEntryIdInvalid); assert(type_has_zero_bits_known(type_entry)); return !type_entry->zero_bits; } bool type_requires_comptime(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: return true; case TypeTableEntryIdArray: return type_requires_comptime(type_entry->data.array.child_type); case TypeTableEntryIdStruct: assert(type_has_zero_bits_known(type_entry)); return type_entry->data.structure.requires_comptime; case TypeTableEntryIdUnion: assert(type_has_zero_bits_known(type_entry)); return type_entry->data.unionation.requires_comptime; case TypeTableEntryIdOptional: return type_requires_comptime(type_entry->data.maybe.child_type); case TypeTableEntryIdErrorUnion: return type_requires_comptime(type_entry->data.error_union.payload_type); case TypeTableEntryIdPointer: if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) { return false; } else { return type_requires_comptime(type_entry->data.pointer.child_type); } case TypeTableEntryIdEnum: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: case TypeTableEntryIdPromise: return false; } zig_unreachable(); } void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { auto entry = g->string_literals_table.maybe_get(str); if (entry != nullptr) { *const_val = *entry->value; return; } const_val->special = ConstValSpecialStatic; const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str)); const_val->data.x_array.s_none.elements = create_const_vals(buf_len(str)); for (size_t i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &const_val->data.x_array.s_none.elements[i]; this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]); } g->string_literals_table.put(str, const_val); } ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str) { ConstExprValue *const_val = create_const_vals(1); init_const_str_lit(g, const_val, str); return const_val; } void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { // first we build the underlying array size_t len_with_null = buf_len(str) + 1; ConstExprValue *array_val = create_const_vals(1); array_val->special = ConstValSpecialStatic; array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null); array_val->data.x_array.s_none.elements = create_const_vals(len_with_null); for (size_t i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &array_val->data.x_array.s_none.elements[i]; this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]); } ConstExprValue *null_char = &array_val->data.x_array.s_none.elements[len_with_null - 1]; null_char->special = ConstValSpecialStatic; null_char->type = g->builtin_types.entry_u8; bigint_init_unsigned(&null_char->data.x_bigint, 0); // then make the pointer point to it const_val->special = ConstValSpecialStatic; // TODO make this `[*]null u8` instead of `[*]u8` const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = 0; const_val->data.x_ptr.data.base_array.is_cstr = true; } ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) { ConstExprValue *const_val = create_const_vals(1); init_const_c_str_lit(g, const_val, str); return const_val; } void init_const_bigint(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *bigint) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_bigint(&const_val->data.x_bigint, bigint); } ConstExprValue *create_const_bigint(TypeTableEntry *type, const BigInt *bigint) { ConstExprValue *const_val = create_const_vals(1); init_const_bigint(const_val, type, bigint); return const_val; } void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_unsigned(&const_val->data.x_bigint, x); const_val->data.x_bigint.is_negative = negative; } ConstExprValue *create_const_unsigned_negative(TypeTableEntry *type, uint64_t x, bool negative) { ConstExprValue *const_val = create_const_vals(1); init_const_unsigned_negative(const_val, type, x, negative); return const_val; } void init_const_usize(CodeGen *g, ConstExprValue *const_val, uint64_t x) { return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false); } ConstExprValue *create_const_usize(CodeGen *g, uint64_t x) { return create_const_unsigned_negative(g->builtin_types.entry_usize, x, false); } void init_const_signed(ConstExprValue *const_val, TypeTableEntry *type, int64_t x) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_signed(&const_val->data.x_bigint, x); } ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) { ConstExprValue *const_val = create_const_vals(1); init_const_signed(const_val, type, x); return const_val; } void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value) { const_val->special = ConstValSpecialStatic; const_val->type = type; if (type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_64(&const_val->data.x_bigfloat, value); } else if (type->id == TypeTableEntryIdFloat) { switch (type->data.floating.bit_count) { case 32: const_val->data.x_f32 = value; break; case 64: const_val->data.x_f64 = value; break; case 128: // if we need this, we should add a function that accepts a float128_t param zig_unreachable(); default: zig_unreachable(); } } else { zig_unreachable(); } } ConstExprValue *create_const_float(TypeTableEntry *type, double value) { ConstExprValue *const_val = create_const_vals(1); init_const_float(const_val, type, value); return const_val; } void init_const_enum(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag) { const_val->special = ConstValSpecialStatic; const_val->type = type; bigint_init_bigint(&const_val->data.x_enum_tag, tag); } ConstExprValue *create_const_enum(TypeTableEntry *type, const BigInt *tag) { ConstExprValue *const_val = create_const_vals(1); init_const_enum(const_val, type, tag); return const_val; } void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value) { const_val->special = ConstValSpecialStatic; const_val->type = g->builtin_types.entry_bool; const_val->data.x_bool = value; } ConstExprValue *create_const_bool(CodeGen *g, bool value) { ConstExprValue *const_val = create_const_vals(1); init_const_bool(g, const_val, value); return const_val; } void init_const_runtime(ConstExprValue *const_val, TypeTableEntry *type) { const_val->special = ConstValSpecialRuntime; const_val->type = type; } ConstExprValue *create_const_runtime(TypeTableEntry *type) { ConstExprValue *const_val = create_const_vals(1); init_const_runtime(const_val, type); return const_val; } void init_const_type(CodeGen *g, ConstExprValue *const_val, TypeTableEntry *type_value) { const_val->special = ConstValSpecialStatic; const_val->type = g->builtin_types.entry_type; const_val->data.x_type = type_value; } ConstExprValue *create_const_type(CodeGen *g, TypeTableEntry *type_value) { ConstExprValue *const_val = create_const_vals(1); init_const_type(g, const_val, type_value); return const_val; } void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, size_t start, size_t len, bool is_const) { assert(array_val->type->id == TypeTableEntryIdArray); TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type, is_const, false, PtrLenUnknown, get_abi_alignment(g, array_val->type->data.array.child_type), 0, 0); const_val->special = ConstValSpecialStatic; const_val->type = get_slice_type(g, ptr_type); const_val->data.x_struct.fields = create_const_vals(2); init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const, PtrLenUnknown); init_const_usize(g, &const_val->data.x_struct.fields[slice_len_index], len); } ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t start, size_t len, bool is_const) { ConstExprValue *const_val = create_const_vals(1); init_const_slice(g, const_val, array_val, start, len, is_const); return const_val; } void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, size_t elem_index, bool is_const, PtrLen ptr_len) { assert(array_val->type->id == TypeTableEntryIdArray); TypeTableEntry *child_type = array_val->type->data.array.child_type; const_val->special = ConstValSpecialStatic; const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false, ptr_len, get_abi_alignment(g, child_type), 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = elem_index; } ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const, PtrLen ptr_len) { ConstExprValue *const_val = create_const_vals(1); init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len); return const_val; } void init_const_ptr_ref(CodeGen *g, ConstExprValue *const_val, ConstExprValue *pointee_val, bool is_const) { const_val->special = ConstValSpecialStatic; const_val->type = get_pointer_to_type(g, pointee_val->type, is_const); const_val->data.x_ptr.special = ConstPtrSpecialRef; const_val->data.x_ptr.data.ref.pointee = pointee_val; } ConstExprValue *create_const_ptr_ref(CodeGen *g, ConstExprValue *pointee_val, bool is_const) { ConstExprValue *const_val = create_const_vals(1); init_const_ptr_ref(g, const_val, pointee_val, is_const); return const_val; } void init_const_ptr_hard_coded_addr(CodeGen *g, ConstExprValue *const_val, TypeTableEntry *pointee_type, size_t addr, bool is_const) { const_val->special = ConstValSpecialStatic; const_val->type = get_pointer_to_type(g, pointee_type, is_const); const_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; const_val->data.x_ptr.data.hard_coded_addr.addr = addr; } ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, TypeTableEntry *pointee_type, size_t addr, bool is_const) { ConstExprValue *const_val = create_const_vals(1); init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const); return const_val; } void init_const_arg_tuple(CodeGen *g, ConstExprValue *const_val, size_t arg_index_start, size_t arg_index_end) { const_val->special = ConstValSpecialStatic; const_val->type = g->builtin_types.entry_arg_tuple; const_val->data.x_arg_tuple.start_index = arg_index_start; const_val->data.x_arg_tuple.end_index = arg_index_end; } ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_t arg_index_end) { ConstExprValue *const_val = create_const_vals(1); init_const_arg_tuple(g, const_val, arg_index_start, arg_index_end); return const_val; } void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { TypeTableEntry *wanted_type = const_val->type; if (wanted_type->id == TypeTableEntryIdArray) { const_val->special = ConstValSpecialStatic; const_val->data.x_array.special = ConstArraySpecialUndef; } else if (wanted_type->id == TypeTableEntryIdStruct) { ensure_complete_type(g, wanted_type); if (type_is_invalid(wanted_type)) { return; } const_val->special = ConstValSpecialStatic; size_t field_count = wanted_type->data.structure.src_field_count; const_val->data.x_struct.fields = create_const_vals(field_count); for (size_t i = 0; i < field_count; i += 1) { ConstExprValue *field_val = &const_val->data.x_struct.fields[i]; field_val->type = wanted_type->data.structure.fields[i].type_entry; assert(field_val->type); init_const_undefined(g, field_val); ConstParent *parent = get_const_val_parent(g, field_val); if (parent != nullptr) { parent->id = ConstParentIdStruct; parent->data.p_struct.struct_val = const_val; parent->data.p_struct.field_index = i; } } } else { const_val->special = ConstValSpecialUndef; } } ConstExprValue *create_const_vals(size_t count) { ConstGlobalRefs *global_refs = allocate(count); ConstExprValue *vals = allocate(count); for (size_t i = 0; i < count; i += 1) { vals[i].global_refs = &global_refs[i]; } return vals; } void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdStruct) { if (!type_entry->data.structure.complete) resolve_struct_type(g, type_entry); } else if (type_entry->id == TypeTableEntryIdEnum) { if (!type_entry->data.enumeration.complete) resolve_enum_type(g, type_entry); } else if (type_entry->id == TypeTableEntryIdUnion) { if (!type_entry->data.unionation.complete) resolve_union_type(g, type_entry); } } void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdStruct) { resolve_struct_zero_bits(g, type_entry); } else if (type_entry->id == TypeTableEntryIdEnum) { resolve_enum_zero_bits(g, type_entry); } else if (type_entry->id == TypeTableEntryIdUnion) { resolve_union_zero_bits(g, type_entry); } } bool ir_get_var_is_comptime(VariableTableEntry *var) { if (!var->is_comptime) return false; if (var->is_comptime->other) return var->is_comptime->other->value.data.x_bool; return var->is_comptime->value.data.x_bool; } bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { if (a->data.x_ptr.special != b->data.x_ptr.special) return false; if (a->data.x_ptr.mut != b->data.x_ptr.mut) return false; switch (a->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); case ConstPtrSpecialRef: if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee) return false; return true; case ConstPtrSpecialBaseArray: if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val && a->data.x_ptr.data.base_array.array_val->global_refs != b->data.x_ptr.data.base_array.array_val->global_refs) { return false; } if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index) return false; if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr) return false; return true; case ConstPtrSpecialBaseStruct: if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val && a->data.x_ptr.data.base_struct.struct_val->global_refs != b->data.x_ptr.data.base_struct.struct_val->global_refs) { return false; } if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) return false; return true; case ConstPtrSpecialHardCodedAddr: if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) return false; return true; case ConstPtrSpecialDiscard: return true; case ConstPtrSpecialFunction: return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; } zig_unreachable(); } bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { assert(a->type->id == b->type->id); assert(a->special == ConstValSpecialStatic); assert(b->special == ConstValSpecialStatic); switch (a->type->id) { case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdEnum: return bigint_cmp(&a->data.x_enum_tag, &b->data.x_enum_tag) == CmpEQ; case TypeTableEntryIdUnion: { ConstUnionValue *union1 = &a->data.x_union; ConstUnionValue *union2 = &b->data.x_union; if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) { TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag); assert(field != nullptr); if (type_has_bits(field->type_entry)) { zig_panic("TODO const expr analyze union field value for equality"); } else { return true; } } return false; } case TypeTableEntryIdMetaType: return a->data.x_type == b->data.x_type; case TypeTableEntryIdVoid: return true; case TypeTableEntryIdErrorSet: return a->data.x_err_set->value == b->data.x_err_set->value; case TypeTableEntryIdBool: return a->data.x_bool == b->data.x_bool; case TypeTableEntryIdFloat: assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count); switch (a->type->data.floating.bit_count) { case 32: return a->data.x_f32 == b->data.x_f32; case 64: return a->data.x_f64 == b->data.x_f64; case 128: return f128M_eq(&a->data.x_f128, &b->data.x_f128); default: zig_unreachable(); } case TypeTableEntryIdComptimeFloat: return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; case TypeTableEntryIdInt: case TypeTableEntryIdComptimeInt: return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: case TypeTableEntryIdFn: return const_values_equal_ptr(a, b); case TypeTableEntryIdArray: zig_panic("TODO"); case TypeTableEntryIdStruct: for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { ConstExprValue *field_a = &a->data.x_struct.fields[i]; ConstExprValue *field_b = &b->data.x_struct.fields[i]; if (!const_values_equal(field_a, field_b)) return false; } return true; case TypeTableEntryIdUndefined: zig_panic("TODO"); case TypeTableEntryIdNull: zig_panic("TODO"); case TypeTableEntryIdOptional: if (get_codegen_ptr_type(a->type) != nullptr) return const_values_equal_ptr(a, b); if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) { return (a->data.x_optional == nullptr && b->data.x_optional == nullptr); } else { return const_values_equal(a->data.x_optional, b->data.x_optional); } case TypeTableEntryIdErrorUnion: zig_panic("TODO"); case TypeTableEntryIdNamespace: return a->data.x_import == b->data.x_import; case TypeTableEntryIdBlock: return a->data.x_block == b->data.x_block; case TypeTableEntryIdArgTuple: return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index && a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index; case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdUnreachable: case TypeTableEntryIdPromise: zig_unreachable(); } zig_unreachable(); } void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max) { assert(int_type->id == TypeTableEntryIdInt); if (int_type->data.integral.bit_count == 0) { bigint_init_unsigned(bigint, 0); return; } if (is_max) { // is_signed=true (1 << (bit_count - 1)) - 1 // is_signed=false (1 << (bit_count - 0)) - 1 BigInt one = {0}; bigint_init_unsigned(&one, 1); size_t shift_amt = int_type->data.integral.bit_count - (int_type->data.integral.is_signed ? 1 : 0); BigInt bit_count_bi = {0}; bigint_init_unsigned(&bit_count_bi, shift_amt); BigInt shifted_bi = {0}; bigint_shl(&shifted_bi, &one, &bit_count_bi); bigint_sub(bigint, &shifted_bi, &one); } else if (int_type->data.integral.is_signed) { // - (1 << (bit_count - 1)) BigInt one = {0}; bigint_init_unsigned(&one, 1); BigInt bit_count_bi = {0}; bigint_init_unsigned(&bit_count_bi, int_type->data.integral.bit_count - 1); BigInt shifted_bi = {0}; bigint_shl(&shifted_bi, &one, &bit_count_bi); bigint_negate(bigint, &shifted_bi); } else { bigint_init_unsigned(bigint, 0); } } void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max) { if (type_entry->id == TypeTableEntryIdInt) { const_val->special = ConstValSpecialStatic; eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max); } else if (type_entry->id == TypeTableEntryIdFloat) { zig_panic("TODO analyze_min_max_value float"); } else if (type_entry->id == TypeTableEntryIdBool) { const_val->special = ConstValSpecialStatic; const_val->data.x_bool = is_max; } else if (type_entry->id == TypeTableEntryIdVoid) { // nothing to do } else { zig_unreachable(); } } void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeTableEntry *type_entry) { switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); case ConstPtrSpecialRef: case ConstPtrSpecialBaseStruct: buf_appendf(buf, "*"); render_const_value(g, buf, const_ptr_pointee(g, const_val)); return; case ConstPtrSpecialBaseArray: if (const_val->data.x_ptr.data.base_array.is_cstr) { buf_appendf(buf, "*(c str lit)"); return; } else { buf_appendf(buf, "*"); render_const_value(g, buf, const_ptr_pointee(g, const_val)); return; } case ConstPtrSpecialHardCodedAddr: buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name), const_val->data.x_ptr.data.hard_coded_addr.addr); return; case ConstPtrSpecialDiscard: buf_append_str(buf, "*_"); return; case ConstPtrSpecialFunction: { FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); return; } } zig_unreachable(); } void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: buf_appendf(buf, "(runtime value)"); return; case ConstValSpecialUndef: buf_appendf(buf, "undefined"); return; case ConstValSpecialStatic: break; } assert(const_val->type); TypeTableEntry *type_entry = const_val->type; switch (type_entry->id) { case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdInvalid: buf_appendf(buf, "(invalid)"); return; case TypeTableEntryIdVoid: buf_appendf(buf, "{}"); return; case TypeTableEntryIdComptimeFloat: bigfloat_append_buf(buf, &const_val->data.x_bigfloat); return; case TypeTableEntryIdFloat: switch (type_entry->data.floating.bit_count) { case 32: buf_appendf(buf, "%f", const_val->data.x_f32); return; case 64: buf_appendf(buf, "%f", const_val->data.x_f64); return; case 128: { const size_t extra_len = 100; size_t old_len = buf_len(buf); buf_resize(buf, old_len + extra_len); float64_t f64_value = f128M_to_f64(&const_val->data.x_f128); double double_value; memcpy(&double_value, &f64_value, sizeof(double)); // TODO actual f128 printing to decimal int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value); assert(len > 0); buf_resize(buf, old_len + len); return; } default: zig_unreachable(); } case TypeTableEntryIdComptimeInt: case TypeTableEntryIdInt: bigint_append_buf(buf, &const_val->data.x_bigint, 10); return; case TypeTableEntryIdMetaType: buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name)); return; case TypeTableEntryIdUnreachable: buf_appendf(buf, "unreachable"); return; case TypeTableEntryIdBool: { const char *value = const_val->data.x_bool ? "true" : "false"; buf_appendf(buf, "%s", value); return; } case TypeTableEntryIdFn: { assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name)); return; } case TypeTableEntryIdPointer: return render_const_val_ptr(g, buf, const_val, type_entry); case TypeTableEntryIdBlock: { AstNode *node = const_val->data.x_block->source_node; buf_appendf(buf, "(scope:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", node->line + 1, node->column + 1); return; } case TypeTableEntryIdArray: { TypeTableEntry *child_type = type_entry->data.array.child_type; uint64_t len = type_entry->data.array.len; if (const_val->data.x_array.special == ConstArraySpecialUndef) { buf_append_str(buf, "undefined"); return; } // if it's []u8, assume UTF-8 and output a string if (child_type->id == TypeTableEntryIdInt && child_type->data.integral.bit_count == 8 && !child_type->data.integral.is_signed) { buf_append_char(buf, '"'); for (uint64_t i = 0; i < len; i += 1) { ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i]; uint64_t big_c = bigint_as_unsigned(&child_value->data.x_bigint); assert(big_c <= UINT8_MAX); uint8_t c = (uint8_t)big_c; if (c == '"') { buf_append_str(buf, "\\\""); } else { buf_append_char(buf, c); } } buf_append_char(buf, '"'); return; } buf_appendf(buf, "%s{", buf_ptr(&type_entry->name)); for (uint64_t i = 0; i < len; i += 1) { if (i != 0) buf_appendf(buf, ","); ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i]; render_const_value(g, buf, child_value); } buf_appendf(buf, "}"); return; } case TypeTableEntryIdNull: { buf_appendf(buf, "null"); return; } case TypeTableEntryIdUndefined: { buf_appendf(buf, "undefined"); return; } case TypeTableEntryIdOptional: { if (get_codegen_ptr_type(const_val->type) != nullptr) return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); if (const_val->data.x_optional) { render_const_value(g, buf, const_val->data.x_optional); } else { buf_appendf(buf, "null"); } return; } case TypeTableEntryIdNamespace: { ImportTableEntry *import = const_val->data.x_import; if (import->c_import_node) { buf_appendf(buf, "(namespace from C import)"); } else { buf_appendf(buf, "(namespace: %s)", buf_ptr(import->path)); } return; } case TypeTableEntryIdBoundFn: { FnTableEntry *fn_entry = const_val->data.x_bound_fn.fn; buf_appendf(buf, "(bound fn %s)", buf_ptr(&fn_entry->symbol_name)); return; } case TypeTableEntryIdStruct: { buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name)); return; } case TypeTableEntryIdEnum: { TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag); buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name)); return; } case TypeTableEntryIdErrorUnion: { buf_appendf(buf, "(error union %s constant)", buf_ptr(&type_entry->name)); return; } case TypeTableEntryIdUnion: { buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name)); return; } case TypeTableEntryIdErrorSet: { buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); return; } case TypeTableEntryIdArgTuple: { buf_appendf(buf, "(args value)"); return; } case TypeTableEntryIdPromise: zig_unreachable(); } zig_unreachable(); } TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->is_copyable = true; entry->type_ref = (size_in_bits == 0) ? LLVMVoidType() : LLVMIntType(size_in_bits); entry->zero_bits = (size_in_bits == 0); const char u_or_i = is_signed ? 'i' : 'u'; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "%c%" PRIu32, u_or_i, size_in_bits); unsigned dwarf_tag; if (is_signed) { if (size_in_bits == 8) { dwarf_tag = ZigLLVMEncoding_DW_ATE_signed_char(); } else { dwarf_tag = ZigLLVMEncoding_DW_ATE_signed(); } } else { if (size_in_bits == 8) { dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned_char(); } else { dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned(); } } uint64_t debug_size_in_bits = (size_in_bits == 0) ? 0 : (8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref)); entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), debug_size_in_bits, dwarf_tag); entry->data.integral.is_signed = is_signed; entry->data.integral.bit_count = size_in_bits; return entry; } uint32_t type_id_hash(TypeId x) { switch (x.id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdOpaque: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdOptional: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: zig_unreachable(); case TypeTableEntryIdErrorUnion: return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); case TypeTableEntryIdPointer: return hash_ptr(x.data.pointer.child_type) + ((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) + (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) + (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) + (((uint32_t)x.data.pointer.bit_offset) ^ (uint32_t)2639019452) + (((uint32_t)x.data.pointer.unaligned_bit_count) ^ (uint32_t)529908881); case TypeTableEntryIdArray: return hash_ptr(x.data.array.child_type) + ((uint32_t)x.data.array.size ^ (uint32_t)2122979968); case TypeTableEntryIdInt: return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); } zig_unreachable(); } bool type_id_eql(TypeId a, TypeId b) { if (a.id != b.id) return false; switch (a.id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdOptional: case TypeTableEntryIdPromise: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdErrorUnion: return a.data.error_union.err_set_type == b.data.error_union.err_set_type && a.data.error_union.payload_type == b.data.error_union.payload_type; case TypeTableEntryIdPointer: return a.data.pointer.child_type == b.data.pointer.child_type && a.data.pointer.ptr_len == b.data.pointer.ptr_len && a.data.pointer.is_const == b.data.pointer.is_const && a.data.pointer.is_volatile == b.data.pointer.is_volatile && a.data.pointer.alignment == b.data.pointer.alignment && a.data.pointer.bit_offset == b.data.pointer.bit_offset && a.data.pointer.unaligned_bit_count == b.data.pointer.unaligned_bit_count; case TypeTableEntryIdArray: return a.data.array.child_type == b.data.array.child_type && a.data.array.size == b.data.array.size; case TypeTableEntryIdInt: return a.data.integer.is_signed == b.data.integer.is_signed && a.data.integer.bit_count == b.data.integer.bit_count; } zig_unreachable(); } uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { switch (x.id) { case ZigLLVMFnIdCtz: return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934; case ZigLLVMFnIdClz: return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; case ZigLLVMFnIdFloor: return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168; case ZigLLVMFnIdCeil: return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089; case ZigLLVMFnIdSqrt: return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385; case ZigLLVMFnIdOverflowArithmetic: return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) + ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) + ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820); } zig_unreachable(); } bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { if (a.id != b.id) return false; switch (a.id) { case ZigLLVMFnIdCtz: return a.data.ctz.bit_count == b.data.ctz.bit_count; case ZigLLVMFnIdClz: return a.data.clz.bit_count == b.data.clz.bit_count; case ZigLLVMFnIdFloor: case ZigLLVMFnIdCeil: case ZigLLVMFnIdSqrt: return a.data.floating.bit_count == b.data.floating.bit_count; case ZigLLVMFnIdOverflowArithmetic: return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) && (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed); } zig_unreachable(); } void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdArray); if (const_val->data.x_array.special == ConstArraySpecialUndef) { const_val->data.x_array.special = ConstArraySpecialNone; size_t elem_count = const_val->type->data.array.len; const_val->data.x_array.s_none.elements = create_const_vals(elem_count); for (size_t i = 0; i < elem_count; i += 1) { ConstExprValue *element_val = &const_val->data.x_array.s_none.elements[i]; element_val->type = const_val->type->data.array.child_type; init_const_undefined(g, element_val); ConstParent *parent = get_const_val_parent(g, element_val); if (parent != nullptr) { parent->id = ConstParentIdArray; parent->data.p_array.array_val = const_val; parent->data.p_array.elem_index = i; } } } } ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { assert(value->type); TypeTableEntry *type_entry = value->type; if (type_entry->id == TypeTableEntryIdArray) { expand_undef_array(g, value); return &value->data.x_array.s_none.parent; } else if (type_entry->id == TypeTableEntryIdStruct) { return &value->data.x_struct.parent; } else if (type_entry->id == TypeTableEntryIdUnion) { return &value->data.x_union.parent; } return nullptr; } static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdMetaType, TypeTableEntryIdVoid, TypeTableEntryIdBool, TypeTableEntryIdUnreachable, TypeTableEntryIdInt, TypeTableEntryIdFloat, TypeTableEntryIdPointer, TypeTableEntryIdArray, TypeTableEntryIdStruct, TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefined, TypeTableEntryIdNull, TypeTableEntryIdOptional, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, TypeTableEntryIdEnum, TypeTableEntryIdUnion, TypeTableEntryIdFn, TypeTableEntryIdNamespace, TypeTableEntryIdBlock, TypeTableEntryIdBoundFn, TypeTableEntryIdArgTuple, TypeTableEntryIdOpaque, TypeTableEntryIdPromise, }; TypeTableEntryId type_id_at_index(size_t index) { assert(index < array_length(all_type_ids)); return all_type_ids[index]; } size_t type_id_len() { return array_length(all_type_ids); } size_t type_id_index(TypeTableEntry *entry) { switch (entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: return 0; case TypeTableEntryIdVoid: return 1; case TypeTableEntryIdBool: return 2; case TypeTableEntryIdUnreachable: return 3; case TypeTableEntryIdInt: return 4; case TypeTableEntryIdFloat: return 5; case TypeTableEntryIdPointer: return 6; case TypeTableEntryIdArray: return 7; case TypeTableEntryIdStruct: if (entry->data.structure.is_slice) return 6; return 8; case TypeTableEntryIdComptimeFloat: return 9; case TypeTableEntryIdComptimeInt: return 10; case TypeTableEntryIdUndefined: return 11; case TypeTableEntryIdNull: return 12; case TypeTableEntryIdOptional: return 13; case TypeTableEntryIdErrorUnion: return 14; case TypeTableEntryIdErrorSet: return 15; case TypeTableEntryIdEnum: return 16; case TypeTableEntryIdUnion: return 17; case TypeTableEntryIdFn: return 18; case TypeTableEntryIdNamespace: return 19; case TypeTableEntryIdBlock: return 20; case TypeTableEntryIdBoundFn: return 21; case TypeTableEntryIdArgTuple: return 22; case TypeTableEntryIdOpaque: return 23; case TypeTableEntryIdPromise: return 24; } zig_unreachable(); } const char *type_id_name(TypeTableEntryId id) { switch (id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: return "Type"; case TypeTableEntryIdVoid: return "Void"; case TypeTableEntryIdBool: return "Bool"; case TypeTableEntryIdUnreachable: return "NoReturn"; case TypeTableEntryIdInt: return "Int"; case TypeTableEntryIdFloat: return "Float"; case TypeTableEntryIdPointer: return "Pointer"; case TypeTableEntryIdArray: return "Array"; case TypeTableEntryIdStruct: return "Struct"; case TypeTableEntryIdComptimeFloat: return "ComptimeFloat"; case TypeTableEntryIdComptimeInt: return "ComptimeInt"; case TypeTableEntryIdUndefined: return "Undefined"; case TypeTableEntryIdNull: return "Null"; case TypeTableEntryIdOptional: return "Optional"; case TypeTableEntryIdErrorUnion: return "ErrorUnion"; case TypeTableEntryIdErrorSet: return "ErrorSet"; case TypeTableEntryIdEnum: return "Enum"; case TypeTableEntryIdUnion: return "Union"; case TypeTableEntryIdFn: return "Fn"; case TypeTableEntryIdNamespace: return "Namespace"; case TypeTableEntryIdBlock: return "Block"; case TypeTableEntryIdBoundFn: return "BoundFn"; case TypeTableEntryIdArgTuple: return "ArgTuple"; case TypeTableEntryIdOpaque: return "Opaque"; case TypeTableEntryIdPromise: return "Promise"; } zig_unreachable(); } LinkLib *create_link_lib(Buf *name) { LinkLib *link_lib = allocate(1); link_lib->name = name; return link_lib; } LinkLib *add_link_lib(CodeGen *g, Buf *name) { bool is_libc = buf_eql_str(name, "c"); if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { return existing_lib; } } LinkLib *link_lib = create_link_lib(name); g->link_libs_list.append(link_lib); if (is_libc) g->libc_link_lib = link_lib; return link_lib; } uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { type_ensure_zero_bits_known(g, type_entry); if (type_entry->zero_bits) return 0; // We need to make this function work without requiring ensure_complete_type // so that we can have structs with fields that are pointers to their own type. if (type_entry->id == TypeTableEntryIdStruct) { assert(type_entry->data.structure.abi_alignment != 0); return type_entry->data.structure.abi_alignment; } else if (type_entry->id == TypeTableEntryIdUnion) { assert(type_entry->data.unionation.abi_alignment != 0); return type_entry->data.unionation.abi_alignment; } else if (type_entry->id == TypeTableEntryIdOpaque) { return 1; } else { return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); } } TypeTableEntry *get_align_amt_type(CodeGen *g) { if (g->align_amt_type == nullptr) { // according to LLVM the maximum alignment is 1 << 29. g->align_amt_type = get_int_type(g, false, 29); } return g->align_amt_type; } uint32_t type_ptr_hash(const TypeTableEntry *ptr) { return hash_ptr((void*)ptr); } bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) { return a == b; } ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); resolve_top_level_decl(codegen, tld, false, nullptr); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; ConstExprValue *var_value = tld_var->var->value; assert(var_value != nullptr); return var_value; } bool type_is_global_error_set(TypeTableEntry *err_set_type) { assert(err_set_type->id == TypeTableEntryIdErrorSet); assert(err_set_type->data.error_set.infer_fn == nullptr); return err_set_type->data.error_set.err_count == UINT32_MAX; } uint32_t get_coro_frame_align_bytes(CodeGen *g) { return g->pointer_size_bytes * 2; } bool type_can_fail(TypeTableEntry *type_entry) { return type_entry->id == TypeTableEntryIdErrorUnion || type_entry->id == TypeTableEntryIdErrorSet; } bool fn_type_can_fail(FnTypeId *fn_type_id) { return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync; }