/* * 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 "zig_llvm.hpp" 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); ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { // if this assert fails, then parseh generated code that // failed semantic analysis, which isn't supposed to happen assert(!node->owner->c_import_node); 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 this assert fails, then parseh generated code that // failed semantic analysis, which isn't supposed to happen assert(!node->owner->c_import_node); 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->label_table.init(1); 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; } Scope *create_loop_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr); ScopeLoop *scope = allocate(1); init_scope(&scope->base, ScopeIdLoop, node, parent); return &scope->base; } ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) { assert(!node || node->type == NodeTypeFnDef); 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; } 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; } // TODO no reason to limit to 8/16/32/64 static size_t bits_needed_for_unsigned(uint64_t x) { if (x <= UINT8_MAX) { return 8; } else if (x <= UINT16_MAX) { return 16; } else if (x <= UINT32_MAX) { return 32; } else { return 64; } } bool type_is_complete(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: 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 TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: return true; } zig_unreachable(); } bool type_has_zero_bits_known(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: 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 TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: return true; } zig_unreachable(); } uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) { assert(type_is_complete(type_entry)); TypeTableEntry *canon_type = get_underlying_type(type_entry); if (!type_has_bits(type_entry)) return 0; if (canon_type->id == TypeTableEntryIdStruct && canon_type->data.structure.layout == ContainerLayoutPacked) { uint64_t size_in_bits = type_size_bits(g, type_entry); return (size_in_bits + 7) / 8; } else if (canon_type->id == TypeTableEntryIdArray) { TypeTableEntry *canon_child_type = get_underlying_type(canon_type->data.array.child_type); if (canon_child_type->id == TypeTableEntryIdStruct && canon_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)); TypeTableEntry *canon_type = get_underlying_type(type_entry); if (!type_has_bits(type_entry)) return 0; if (canon_type->id == TypeTableEntryIdStruct && canon_type->data.structure.layout == ContainerLayoutPacked) { uint64_t result = 0; for (size_t i = 0; i < canon_type->data.structure.src_field_count; i += 1) { result += type_size_bits(g, canon_type->data.structure.fields[i].type_entry); } return result; } else if (canon_type->id == TypeTableEntryIdArray) { TypeTableEntry *canon_child_type = get_underlying_type(canon_type->data.array.child_type); if (canon_child_type->id == TypeTableEntryIdStruct && canon_child_type->data.structure.layout == ContainerLayoutPacked) { return canon_type->data.array.len * type_size_bits(g, canon_child_type); } } return LLVMSizeOfTypeInBits(g->target_data_ref, canon_type->type_ref); } 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_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_volatile, uint32_t bit_offset, uint32_t unaligned_bit_count) { assert(child_type->id != TypeTableEntryIdInvalid); TypeId type_id = {}; TypeTableEntry **parent_pointer = nullptr; if (unaligned_bit_count != 0 || is_volatile) { 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.bit_offset = bit_offset; type_id.data.pointer.unaligned_bit_count = unaligned_bit_count; 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) return *parent_pointer; } type_ensure_zero_bits_known(g, child_type); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); const char *const_str = is_const ? "const " : ""; const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); if (unaligned_bit_count == 0) { buf_appendf(&entry->name, "&%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); } else { buf_appendf(&entry->name, "&:%" PRIu32 ":%" PRIu32 " %s%s%s", bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } TypeTableEntry *canon_child_type = get_underlying_type(child_type); assert(canon_child_type->id != TypeTableEntryIdInvalid); entry->zero_bits = !type_has_bits(canon_child_type); if (!entry->zero_bits) { 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*LLVMABISizeOfType(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 { entry->di_type = g->builtin_types.entry_void->di_type; } entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; entry->data.pointer.is_volatile = is_volatile; 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, 0, 0); } 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(TypeTableEntryIdMaybe); assert(child_type->type_ref); assert(child_type->di_type); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); if (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn) { // 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 { // 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, child_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_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->error_parent) return child_type->error_parent; TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); assert(child_type->type_ref); assert(child_type->di_type); ensure_complete_type(g, child_type); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "%%%s", buf_ptr(&child_type->name)); entry->data.error.child_type = child_type; if (!type_has_bits(child_type)) { entry->type_ref = g->err_tag_type->type_ref; entry->di_type = g->err_tag_type->di_type; } else { LLVMTypeRef elem_types[] = { g->err_tag_type->type_ref, child_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, g->err_tag_type->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, g->err_tag_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, child_type->type_ref); uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_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, child_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, child_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; } child_type->error_parent = 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; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name)); if (!entry->zero_bits) { entry->type_ref = child_type->type_ref ? LLVMArrayType(child_type->type_ref, 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, 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 *child_type, bool is_const, TypeTableEntry *entry) { TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const); unsigned element_count = 2; 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[slice_ptr_index].name = buf_create_from_str("ptr"); 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 = buf_create_from_str("len"); 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; assert(type_has_zero_bits_known(child_type)); if (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 *child_type, bool is_const) { assert(child_type->id != TypeTableEntryIdInvalid); TypeTableEntry **parent_pointer = &child_type->slice_parent[(is_const ? 1 : 0)]; if (*parent_pointer) { return *parent_pointer; } else if (is_const) { TypeTableEntry *var_peer = get_slice_type(g, child_type, false); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name)); slice_type_common_init(g, child_type, is_const, entry); entry->type_ref = var_peer->type_ref; entry->di_type = var_peer->di_type; entry->data.structure.complete = true; entry->data.structure.zero_bits_known = true; *parent_pointer = entry; return entry; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); // 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 *ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if (ptr_type->data.pointer.is_const) { TypeTableEntry *non_const_child_type = get_slice_type(g, ptr_type->data.pointer.child_type, false); TypeTableEntry *var_peer = get_slice_type(g, non_const_child_type, false); entry->type_ref = var_peer->type_ref; entry->di_type = var_peer->di_type; } } buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name)); slice_type_common_init(g, child_type, is_const, 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*LLVMABISizeOfType(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*LLVMABISizeOfType(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; } else { TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const); unsigned element_count = 2; LLVMTypeRef element_types[] = { pointer_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, pointer_type->type_ref); uint64_t ptr_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, pointer_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*LLVMABISizeOfType(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*LLVMABISizeOfType(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, pointer_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.complete = true; entry->data.structure.zero_bits_known = true; *parent_pointer = entry; return entry; } } TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdTypeDecl); buf_init_from_str(&entry->name, name); entry->type_ref = child_type->type_ref; entry->di_type = child_type->di_type; entry->zero_bits = child_type->zero_bits; entry->data.type_decl.child_type = child_type; entry->data.type_decl.canonical_type = get_underlying_type(child_type); 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->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; } 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; } ensure_complete_type(g, fn_type_id->return_type); TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); fn_type->data.fn.fn_type_id = *fn_type_id; if (fn_type_id->is_cold) { // cold calling convention only works on x86. // but we can add the cold attribute later. if (g->zig_target.arch.arch == ZigLLVM_x86 || g->zig_target.arch.arch == ZigLLVM_x86_64) { fn_type->data.fn.calling_convention = LLVMColdCallConv; } else { fn_type->data.fn.calling_convention = LLVMFastCallConv; } } else if (fn_type_id->is_extern) { fn_type->data.fn.calling_convention = LLVMCCallConv; } else { fn_type->data.fn.calling_convention = LLVMFastCallConv; } bool skip_debug_info = false; // populate the name of the type buf_resize(&fn_type->name, 0); const char *extern_str = fn_type_id->is_extern ? "extern " : ""; const char *naked_str = fn_type_id->is_naked ? "nakedcc " : ""; const char *cold_str = fn_type_id->is_cold ? "coldcc " : ""; buf_appendf(&fn_type->name, "%s%s%sfn(", extern_str, naked_str, cold_str); 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->return_type->id != TypeTableEntryIdVoid) { 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 = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type); // +1 for maybe making the first argument the return value LLVMTypeRef *gen_param_types = allocate(1 + fn_type_id->param_count); // +1 because 0 is the return type and +1 for maybe making first arg ret val ZigLLVMDIType **param_di_types = allocate(2 + 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 (!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; 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_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, 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, 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; } unsigned line = decl_node ? decl_node->line : 0; ImportTableEntry *import = get_scope_import(scope); entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name); entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, ZigLLVMTag_DW_structure_type(), name, ZigLLVMFileToScope(import->di_file), import->di_file, line + 1); buf_init_from_str(&entry->name, name); return entry; } TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdTypeDecl) { return type_entry->data.type_decl.canonical_type; } else { return type_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; } static TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); buf_init_from_str(&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; fn_type_id->is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); fn_type_id->is_naked = fn_proto->is_nakedcc; fn_type_id->is_cold = fn_proto->is_coldcc; fn_type_id->param_count = fn_proto->params.length; fn_type_id->param_info = allocate_nonzero(param_count_alloc); fn_type_id->next_param_index = 0; fn_type_id->is_var_args = fn_proto->is_var_args; } static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) { 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_inline = param_node->data.param_decl.is_inline; bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_inline) { if (fn_type_id.is_extern) { add_node_error(g, param_node, buf_sprintf("comptime parameter not allowed in extern function")); 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.is_extern) { fn_type_id.param_count = fn_type_id.next_param_index; continue; } else { return get_generic_fn_type(g, &fn_type_id); } } TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); switch (type_entry->id) { case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdArgTuple: 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 TypeTableEntryIdVar: if (fn_type_id.is_extern) { add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type 'var' not allowed in extern function")); return g->builtin_types.entry_invalid; } return get_generic_fn_type(g, &fn_type_id); case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: 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 inline", 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 TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdEnumTag: 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; } fn_type_id.return_type = analyze_type_expr(g, child_scope, fn_proto->return_type); switch (fn_type_id.return_type->id) { case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdArgTuple: 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 TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: if (fn_type_id.is_extern) { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in extern function", buf_ptr(&fn_type_id.return_type->name))); 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 TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdEnumTag: break; } 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; case TypeTableEntryIdTypeDecl: return type_is_invalid(type_entry->data.type_decl.canonical_type); default: return false; } zig_unreachable(); } static TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnumTag); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "@enumTagType(%s)", buf_ptr(&enum_type->name)); entry->data.enum_tag.enum_type = enum_type; entry->data.enum_tag.int_type = int_type; entry->type_ref = int_type->type_ref; entry->di_type = int_type->di_type; entry->zero_bits = int_type->zero_bits; return entry; } static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { // if you change this logic you likely must also change similar logic in parseh.cpp assert(enum_type->id == TypeTableEntryIdEnum); if (enum_type->data.enumeration.complete) return; resolve_enum_zero_bits(g, enum_type); if (enum_type->data.enumeration.is_invalid) 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); uint32_t gen_field_count = enum_type->data.enumeration.gen_field_count; ZigLLVMDIType **union_inner_di_types = allocate(gen_field_count); TypeTableEntry *biggest_union_member = nullptr; uint64_t biggest_align_in_bits = 0; uint64_t biggest_union_member_size_in_bits = 0; 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) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; TypeTableEntry *field_type = type_enum_field->type_entry; di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i); ensure_complete_type(g, field_type); if (field_type->id == TypeTableEntryIdInvalid) { enum_type->data.enumeration.is_invalid = true; continue; } if (!type_has_bits(field_type)) continue; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref); assert(debug_size_in_bits > 0); assert(debug_align_in_bits > 0); union_inner_di_types[type_enum_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(enum_type->di_type), buf_ptr(type_enum_field->name), import->di_file, field_node->line + 1, debug_size_in_bits, debug_align_in_bits, 0, 0, field_type->di_type); biggest_align_in_bits = max(biggest_align_in_bits, debug_align_in_bits); if (!biggest_union_member || debug_size_in_bits > biggest_union_member_size_in_bits) { biggest_union_member = field_type; biggest_union_member_size_in_bits = debug_size_in_bits; } } // unset temporary flag enum_type->data.enumeration.embedded_in_current = false; enum_type->data.enumeration.complete = true; if (!enum_type->data.enumeration.is_invalid) { enum_type->data.enumeration.union_type = biggest_union_member; TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); enum_type->data.enumeration.tag_type = tag_type_entry; if (biggest_union_member) { // create llvm type for union LLVMTypeRef union_element_type = biggest_union_member->type_ref; LLVMTypeRef union_type_ref = LLVMStructType(&union_element_type, 1, false); // create llvm type for root struct LLVMTypeRef root_struct_element_types[] = { tag_type_entry->type_ref, union_type_ref, }; LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); // create debug type for tag uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref); ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum", import->di_file, decl_node->line + 1, tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, tag_type_entry->di_type, ""); // create debug type for union ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion", import->di_file, decl_node->line + 1, biggest_union_member_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, gen_field_count, 0, ""); // create debug types for members of root struct uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, 0); ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(enum_type->di_type), "tag_field", import->di_file, decl_node->line + 1, tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, 0, tag_di_type); uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, 1); ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(enum_type->di_type), "union_field", import->di_file, decl_node->line + 1, biggest_union_member_size_in_bits, biggest_align_in_bits, union_offset_in_bits, 0, union_di_type); // create debug type for root struct ZigLLVMDIType *di_root_members[] = { tag_member_di_type, union_member_di_type, }; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), import->di_file, decl_node->line + 1, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_root_members, 2, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); enum_type->di_type = replacement_di_type; } else { // create llvm type for root struct enum_type->type_ref = tag_type_entry->type_ref; // create debug type for tag uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref); ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), import->di_file, decl_node->line + 1, tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, tag_type_entry->di_type, ""); ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); enum_type->di_type = tag_di_type; } } } static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { TypeTableEntry *canon_type = get_underlying_type(type_entry); switch (canon_type->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdEnumTag: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: return false; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: return true; case TypeTableEntryIdStruct: return canon_type->data.structure.layout == ContainerLayoutPacked; case TypeTableEntryIdMaybe: { TypeTableEntry *canon_child_type = get_underlying_type(canon_type->data.maybe.child_type); return canon_child_type->id == TypeTableEntryIdPointer || canon_child_type->id == TypeTableEntryIdFn; } } zig_unreachable(); } static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { // if you change the logic of this function likely you must make a similar change in // parseh.cpp 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); 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(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(full_bit_count); size_t store_bit_count = 8 * LLVMStoreSizeOfType(g->target_data_ref, int_type_ref); element_types[gen_field_index] = LLVMIntType(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(); ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, g->builtin_types.entry_void->di_type); struct_type->di_type = g->builtin_types.entry_void->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 = gen_field_count; LLVMStructSetBody(struct_type->type_ref, element_types, gen_field_count, packed); 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, 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, 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, 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, decl_node->line + 1, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 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) { zig_panic("TODO"); } 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; 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 = decl_node->data.container_decl.fields.length; enum_type->data.enumeration.src_field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); Scope *scope = &enum_type->data.enumeration.decls_scope->base; 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); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; type_enum_field->name = field_node->data.struct_field.name; TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); type_enum_field->type_entry = field_type; type_enum_field->value = i; type_ensure_zero_bits_known(g, field_type); if (field_type->id == TypeTableEntryIdInvalid) { enum_type->data.enumeration.is_invalid = true; continue; } if (!type_has_bits(field_type)) continue; type_enum_field->gen_index = gen_field_index; gen_field_index += 1; } enum_type->data.enumeration.zero_bits_loop_flag = false; enum_type->data.enumeration.gen_field_count = gen_field_index; enum_type->zero_bits = (gen_field_index == 0 && field_count < 2); 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) { struct_type->data.structure.zero_bits_known = true; 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 = field_count; struct_type->data.structure.fields = allocate(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; 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; type_ensure_zero_bits_known(g, field_type); if (type_is_invalid(field_type)) { struct_type->data.structure.is_invalid = true; continue; } if (!type_has_bits(field_type)) continue; 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 = 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) { zig_panic("TODO resolve_union_zero_bits"); } 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, bool internal_linkage) { 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; fn_entry->internal_linkage = internal_linkage; 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; bool internal_linkage = (fn_proto->visib_mod != VisibModExport && !proto_node->data.fn_proto.is_extern); FnTableEntry *fn_entry = create_fn_raw(inline_value, internal_linkage); fn_entry->proto_node = proto_node; fn_entry->fn_def_node = proto_node->data.fn_proto.fn_def_node; 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) -> unreachable', found '%s'", buf_ptr(&fn_type->name))); } static void typecheck_panic_fn(CodeGen *g) { assert(g->panic_fn); AstNode *proto_node = g->panic_fn->proto_node; assert(proto_node->type == NodeTypeFnProto); TypeTableEntry *fn_type = g->panic_fn->type_entry; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (fn_type_id->param_count != 1) { return wrong_panic_prototype(g, proto_node, fn_type); } TypeTableEntry *const_u8_slice = get_slice_type(g, g->builtin_types.entry_u8, true); if (fn_type_id->param_info[0].type != const_u8_slice) { 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); } } static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { ImportTableEntry *import = tld_fn->base.import; AstNode *proto_node = tld_fn->base.source_node; assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; AstNode *fn_def_node = fn_proto->fn_def_node; FnTableEntry *fn_table_entry = create_fn(tld_fn->base.source_node); get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_'); tld_fn->fn_entry = fn_table_entry; if (fn_table_entry->fn_def_node) { fn_table_entry->fndef_scope = create_fndef_scope( fn_table_entry->fn_def_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 (buf_len(param_node->data.param_decl.name) == 0) { add_node_error(g, param_node, buf_sprintf("missing parameter name")); } } } 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, proto_node, child_scope); if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) { tld_fn->base.resolution = TldResolutionInvalid; return; } if (!fn_table_entry->type_entry->data.fn.is_generic) { g->fn_protos.append(fn_table_entry); if (fn_def_node) g->fn_defs.append(fn_table_entry); if (import == g->root_import && scope_is_root_decls(tld_fn->base.parent_scope)) { if (buf_eql_str(&fn_table_entry->symbol_name, "main")) { g->main_fn = fn_table_entry; if (!g->link_libc && tld_fn->base.visib_mod != VisibModExport) { TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; if (actual_return_type != err_void) { add_node_error(g, fn_proto->return_type, buf_sprintf("expected return type of main to be '%%void', instead is '%s'", buf_ptr(&actual_return_type->name))); } } } else if (buf_eql_str(&fn_table_entry->symbol_name, "panic")) { g->panic_fn = fn_table_entry; typecheck_panic_fn(g); } } else if (import->package == g->panic_package && scope_is_root_decls(tld_fn->base.parent_scope)) { if (buf_eql_str(&fn_table_entry->symbol_name, "panic")) { g->panic_fn = fn_table_entry; typecheck_panic_fn(g); } } } } static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { if (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport || (buf_eql_str(tld->name, "panic") && (decls_scope->import->package == g->panic_package || decls_scope->import == g->root_import))) { g->resolve_queue.append(tld); } 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; } } static void preview_error_value_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeErrorValueDecl); if (node->data.error_value_decl.visib_mod != VisibModPrivate) { add_node_error(g, node, buf_sprintf("error values require no visibility modifier")); } ErrorTableEntry *err = allocate(1); err->decl_node = node; buf_init_from_buf(&err->name, node->data.error_value_decl.name); auto existing_entry = g->error_table.maybe_get(&err->name); if (existing_entry) { // duplicate error definitions allowed and they get the same value err->value = existing_entry->value->value; } else { size_t error_value_count = g->error_decls.length; assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)g->err_tag_type->data.integral.bit_count)); err->value = error_value_count; g->error_decls.append(node); g->error_table.put(&err->name, err); } node->data.error_value_decl.err = err; } 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 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); add_top_level_decl(g, decls_scope, &tld_var->base); break; } case NodeTypeTypeDecl: { Buf *name = node->data.type_decl.symbol; VisibMod visib_mod = node->data.type_decl.visib_mod; TldTypeDef *tld_typedef = allocate(1); init_tld(&tld_typedef->base, TldIdTypeDef, name, visib_mod, node, &decls_scope->base); add_top_level_decl(g, decls_scope, &tld_typedef->base); break; } case NodeTypeFnProto: { // if the name is missing, we immediately announce an error Buf *fn_name = node->data.fn_proto.name; if (buf_len(fn_name) == 0) { 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); 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 NodeTypeErrorValueDecl: // error value declarations do not depend on other top level decls preview_error_value_decl(g, node); break; case NodeTypeContainerDecl: case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeBlock: case NodeTypeBinOpExpr: case NodeTypeUnwrapErrorExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeThisLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeCompTime: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: case NodeTypeContainerInitExpr: case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeTypeLiteral: case NodeTypeVarLiteral: case NodeTypeTryExpr: 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) { TypeTableEntry *underlying_type = get_underlying_type(type_entry); switch (underlying_type->id) { case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdVar: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&underlying_type->name))); return g->builtin_types.entry_invalid; case TypeTableEntryIdNamespace: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: case TypeTableEntryIdEnumTag: 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) { 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; 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 { Tld *tld = find_decl(g, parent_scope, name); if (tld && tld->id != TldIdVar) { 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) { AstNodeVariableDeclaration *var_decl = &tld_var->base.source_node->data.variable_declaration; bool is_const = var_decl->is_const; bool is_export = (tld_var->base.visib_mod == VisibModExport); bool is_extern = var_decl->is_extern; 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); } AstNode *source_node = tld_var->base.source_node; if (is_export && is_extern) { add_node_error(g, source_node, buf_sprintf("variable is both export and extern")); } VarLinkage linkage; if (is_export) { linkage = VarLinkageExport; } else if (is_extern) { linkage = VarLinkageExternal; } else { linkage = VarLinkageInternal; } IrInstruction *init_value = nullptr; 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 == TypeTableEntryIdNumLitFloat || implicit_type->id == TypeTableEntryIdNumLitInt)) { 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 == TypeTableEntryIdNullLit) { 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->var->linkage = linkage; g->global_vars.append(tld_var); } static void resolve_decl_typedef(CodeGen *g, TldTypeDef *tld_typedef) { AstNode *typedef_node = tld_typedef->base.source_node; assert(typedef_node->type == NodeTypeTypeDecl); AstNode *type_node = typedef_node->data.type_decl.child_type; Buf *decl_name = typedef_node->data.type_decl.symbol; TypeTableEntry *child_type = analyze_type_expr(g, tld_typedef->base.parent_scope, type_node); tld_typedef->type_entry = (child_type->id == TypeTableEntryIdInvalid) ? child_type : get_typedecl_type(g, buf_ptr(decl_name), child_type); } void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only) { 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; } else { tld->dep_loop_flag = true; } 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 TldIdTypeDef: { TldTypeDef *tld_typedef = (TldTypeDef *)tld; resolve_decl_typedef(g, tld_typedef); break; } } tld->resolution = TldResolutionOk; tld->dep_loop_flag = false; } bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == actual_type) return true; // pointer const if (expected_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer && (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count) { return types_match_const_cast_only(expected_type->data.pointer.child_type, actual_type->data.pointer.child_type); } // unknown size array const if (expected_type->id == TypeTableEntryIdStruct && actual_type->id == TypeTableEntryIdStruct && expected_type->data.structure.is_slice && actual_type->data.structure.is_slice && (!actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || expected_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const) && (!actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_volatile || expected_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_volatile)) { return types_match_const_cast_only( expected_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type); } // maybe if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { return types_match_const_cast_only( expected_type->data.maybe.child_type, actual_type->data.maybe.child_type); } // error if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { return types_match_const_cast_only( expected_type->data.error.child_type, actual_type->data.error.child_type); } // fn if (expected_type->id == TypeTableEntryIdFn && actual_type->id == TypeTableEntryIdFn) { if (expected_type->data.fn.fn_type_id.is_extern != actual_type->data.fn.fn_type_id.is_extern) { return false; } if (expected_type->data.fn.fn_type_id.is_naked != actual_type->data.fn.fn_type_id.is_naked) { return false; } if (expected_type->data.fn.fn_type_id.is_cold != actual_type->data.fn.fn_type_id.is_cold) { return false; } if (actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable && !types_match_const_cast_only( expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type)) { return false; } if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { return false; } for (size_t i = 0; i < expected_type->data.fn.fn_type_id.param_count; i += 1) { // note it's reversed for parameters FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) { return false; } if (expected_param_info->is_noalias != actual_param_info->is_noalias) { return false; } } return true; } return false; } 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 ScopeIdCompTime: 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) { for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; if (buf_eql_buf(type_enum_field->name, name)) { return type_enum_field; } } return nullptr; } TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->id == TypeTableEntryIdStruct); assert(type_entry->data.structure.complete); for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { TypeStructField *field = &type_entry->data.structure.fields[i]; if (buf_eql_buf(field->name, name)) { return field; } } return nullptr; } static bool is_container(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: 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 TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: return false; } zig_unreachable(); } bool is_container_ref(TypeTableEntry *type_entry) { return (type_entry->id == TypeTableEntryIdPointer) ? 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 (type_entry->id == TypeTableEntryIdPointer) ? 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 TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: zig_unreachable(); } } bool type_is_codegen_pointer(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return true; if (type->id == TypeTableEntryIdFn) return true; if (type->id == TypeTableEntryIdMaybe) { if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return true; if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return true; } return false; } 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; } 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%zu", i); } TypeTableEntry *param_type = param_info->type; bool is_noalias = param_info->is_noalias; if (is_noalias && !type_is_codegen_pointer(param_type)) { add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); } if (fn_type_id->is_extern && handle_is_ptr(param_type)) { add_node_error(g, param_decl_node, buf_sprintf("byvalue types not yet supported on extern function parameters")); } VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, param_name, true, create_const_runtime(param_type)); 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; } } } 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->implicit_return_type = block_return_type; if (block_return_type->id == TypeTableEntryIdInvalid || fn_table_entry->analyzed_executable.invalid) { assert(g->errors.length > 0); fn_table_entry->anal_state = FnAnalStateInvalid; return; } if (g->verbose) { fprintf(stderr, "{ // (analyzed)\n"); ir_print(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->data.fn_proto.return_type; 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); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type)) { add_node_error(g, return_type_node, buf_sprintf("byvalue types not yet supported on extern function return values")); } ir_gen_fn(g, fn_table_entry); if (fn_table_entry->ir_executable.invalid) { fn_table_entry->anal_state = FnAnalStateInvalid; return; } if (g->verbose) { fprintf(stderr, "\n"); ast_render(stderr, fn_table_entry->fn_def_node, 4); fprintf(stderr, "\n{ // (IR)\n"); ir_print(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) { 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; } 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 != TldResolutionUnresolved) return; add_symbols_from_import(g, node, node); } void preview_use_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeUse); 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 *src_dirname, Buf *src_basename, Buf *source_code) { Buf *full_path = buf_alloc(); os_path_join(src_dirname, src_basename, full_path); if (g->verbose) { fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(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(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) { 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 = full_path; import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color, &g->next_node_index); assert(import_entry->root); if (g->verbose) { ast_print(stderr, import_entry->root, 0); } 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); if (is_pub) { if (buf_eql_str(proto_name, "main")) { g->have_exported_main = true; } else if (buf_eql_str(proto_name, "panic")) { g->have_exported_panic = true; } } } } return import_entry; } 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_decls(g, import->decls_scope, import->root); } 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; resolve_top_level_decl(g, tld, pointer_only); } 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); } } } bool is_node_void_expr(AstNode *node) { if (node->type == NodeTypeContainerInitExpr && node->data.container_init_expr.kind == ContainerInitKindArray) { AstNode *type_node = node->data.container_init_expr.type; if (type_node->type == NodeTypeSymbol && buf_eql_str(type_node->data.symbol_expr.symbol, "void")) { return true; } } else if (node->type == NodeTypeBlock && node->data.block.statements.length == 0) { return true; } return false; } TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint8_t size_in_bits) { size_t index; if (size_in_bits == 8) { index = 0; } else if (size_in_bits == 16) { index = 1; } else if (size_in_bits == 32) { index = 2; } else if (size_in_bits == 64) { index = 3; } else { return nullptr; } return &g->builtin_types.entry_int[is_signed ? 0 : 1][index]; } TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint8_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 TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdVar: case TypeTableEntryIdArgTuple: zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdEnumTag: return false; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUnion: return true; case TypeTableEntryIdErrorUnion: return type_has_bits(type_entry->data.error.child_type); case TypeTableEntryIdEnum: assert(type_entry->data.enumeration.complete); return type_entry->data.enumeration.gen_field_count != 0; case TypeTableEntryIdMaybe: return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && type_entry->data.maybe.child_type->id != TypeTableEntryIdFn; case TypeTableEntryIdTypeDecl: return handle_is_ptr(type_entry->data.type_decl.canonical_type); } zig_unreachable(); } void find_libc_include_path(CodeGen *g) { if (!g->libc_include_dir || buf_len(g->libc_include_dir) == 0) { zig_panic("Unable to determine libc include path."); } } 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 || buf_len(g->libc_lib_dir) == 0) { zig_panic("Unable to determine libc lib path."); } if (!g->libc_static_lib_dir || buf_len(g->libc_static_lib_dir) == 0) { zig_panic("Unable to determine libc static lib path."); } } static uint32_t hash_ptr(void *ptr) { return ((uintptr_t)ptr) % UINT32_MAX; } static uint32_t hash_size(size_t x) { return 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 += id->is_extern ? 3349388391 : 0; result += id->is_naked ? 608688877 : 0; result += id->is_cold ? 3605523458 : 0; result += id->is_var_args ? 1931444534 : 0; result += hash_ptr(id->return_type); for (size_t i = 0; i < id->param_count; i += 1) { FnTypeParamInfo *info = &id->param_info[i]; result += info->is_noalias ? 892356923 : 0; result += hash_ptr(info->type); } return result; } bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { if (a->is_extern != b->is_extern || a->is_naked != b->is_naked || a->is_cold != b->is_cold || a->return_type != b->return_type || a->is_var_args != b->is_var_args || a->param_count != b->param_count) { 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(ConstExprValue *const_val) { assert(const_val->special == ConstValSpecialStatic); switch (const_val->type->id) { case TypeTableEntryIdBool: return const_val->data.x_bool ? 127863866 : 215080464; case TypeTableEntryIdMetaType: return hash_ptr(const_val->data.x_type); case TypeTableEntryIdVoid: return 4149439618; case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdEnumTag: return ((uint32_t)(bignum_to_twos_complement(&const_val->data.x_bignum) % UINT32_MAX)) * 1331471175; case TypeTableEntryIdFloat: case TypeTableEntryIdNumLitFloat: return const_val->data.x_bignum.data.x_float * UINT32_MAX; case TypeTableEntryIdArgTuple: return const_val->data.x_arg_tuple.start_index * 281907309 + const_val->data.x_arg_tuple.end_index * 2290442768; case TypeTableEntryIdPointer: { uint32_t hash_val = 0; switch (const_val->data.x_ptr.mut) { case ConstPtrMutRuntimeVar: hash_val += 3500721036; break; case ConstPtrMutComptimeConst: hash_val += 4214318515; break; case ConstPtrMutComptimeVar: hash_val += 1103195694; break; } switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); case ConstPtrSpecialRef: hash_val += 2478261866; hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee); return hash_val; case ConstPtrSpecialBaseArray: hash_val += 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 += 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 += 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; } zig_unreachable(); } case TypeTableEntryIdUndefLit: return 162837799; case TypeTableEntryIdNullLit: 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 TypeTableEntryIdMaybe: if (const_val->data.x_maybe) { return hash_const_val(const_val->data.x_maybe) * 1992916303; } else { return 4016830364; } case TypeTableEntryIdErrorUnion: // TODO better hashing algorithm return 3415065496; case TypeTableEntryIdPureError: // TODO better hashing algorithm return 2630160122; case TypeTableEntryIdEnum: // TODO better hashing algorithm return 31643936; case TypeTableEntryIdFn: return hash_ptr(const_val->data.x_fn); case TypeTableEntryIdTypeDecl: return hash_ptr(const_val->data.x_type); 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: case TypeTableEntryIdVar: 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; } 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 (get_underlying_type(type_entry)->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdUnreachable: case TypeTableEntryIdVar: case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: return true; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUnion: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdEnum: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdEnumTag: case TypeTableEntryIdVoid: return false; } zig_unreachable(); } void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { const_val->special = ConstValSpecialStatic; const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str)); const_val->data.x_array.elements = allocate(buf_len(str)); for (size_t i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &const_val->data.x_array.elements[i]; this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]); } } ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str) { ConstExprValue *const_val = allocate(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 = allocate(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.elements = allocate(len_with_null); for (size_t i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &array_val->data.x_array.elements[i]; this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]); } ConstExprValue *null_char = &array_val->data.x_array.elements[len_with_null - 1]; null_char->special = ConstValSpecialStatic; null_char->type = g->builtin_types.entry_u8; bignum_init_unsigned(&null_char->data.x_bignum, 0); // then make the pointer point to it const_val->special = ConstValSpecialStatic; const_val->type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); 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 = allocate(1); init_const_c_str_lit(g, const_val, str); 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; bignum_init_unsigned(&const_val->data.x_bignum, x); const_val->data.x_bignum.is_negative = negative; } ConstExprValue *create_const_unsigned_negative(TypeTableEntry *type, uint64_t x, bool negative) { ConstExprValue *const_val = allocate(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; bignum_init_signed(&const_val->data.x_bignum, x); } ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) { ConstExprValue *const_val = allocate(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; bignum_init_float(&const_val->data.x_bignum, value); } ConstExprValue *create_const_float(TypeTableEntry *type, double value) { ConstExprValue *const_val = allocate(1); init_const_float(const_val, type, value); return const_val; } void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, uint64_t tag) { const_val->special = ConstValSpecialStatic; const_val->type = type; const_val->data.x_enum.tag = tag; } ConstExprValue *create_const_enum_tag(TypeTableEntry *type, uint64_t tag) { ConstExprValue *const_val = allocate(1); init_const_enum_tag(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 = allocate(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 = allocate(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 = allocate(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); const_val->special = ConstValSpecialStatic; const_val->type = get_slice_type(g, array_val->type->data.array.child_type, is_const); const_val->data.x_struct.fields = allocate(2); init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const); 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 = allocate(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) { 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(g, child_type, is_const); 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) { ConstExprValue *const_val = allocate(1); init_const_ptr_array(g, const_val, array_val, elem_index, is_const); 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 = allocate(1); init_const_ptr_ref(g, const_val, pointee_val, 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 = allocate(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 *canon_wanted_type = get_underlying_type(const_val->type); if (canon_wanted_type->id == TypeTableEntryIdArray) { const_val->special = ConstValSpecialStatic; size_t elem_count = canon_wanted_type->data.array.len; const_val->data.x_array.elements = allocate(elem_count); for (size_t i = 0; i < elem_count; i += 1) { ConstExprValue *element_val = &const_val->data.x_array.elements[i]; element_val->type = canon_wanted_type->data.array.child_type; init_const_undefined(g, element_val); ConstParent *parent = get_const_val_parent(element_val); if (parent != nullptr) { parent->id = ConstParentIdArray; parent->data.p_array.array_val = const_val; parent->data.p_array.elem_index = i; } } } else if (canon_wanted_type->id == TypeTableEntryIdStruct) { ensure_complete_type(g, canon_wanted_type); const_val->special = ConstValSpecialStatic; size_t field_count = canon_wanted_type->data.structure.src_field_count; const_val->data.x_struct.fields = allocate(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 = canon_wanted_type->data.structure.fields[i].type_entry; assert(field_val->type); init_const_undefined(g, field_val); ConstParent *parent = get_const_val_parent(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; } } 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(ConstExprValue *a, ConstExprValue *b) { assert(a->type->id == b->type->id); assert(a->special == ConstValSpecialStatic); assert(b->special == ConstValSpecialStatic); switch (a->type->id) { case TypeTableEntryIdEnum: { ConstEnumValue *enum1 = &a->data.x_enum; ConstEnumValue *enum2 = &b->data.x_enum; if (enum1->tag == enum2->tag) { TypeEnumField *enum_field = &a->type->data.enumeration.fields[enum1->tag]; if (type_has_bits(enum_field->type_entry)) { zig_panic("TODO const expr analyze enum special value for equality"); } else { return true; } } return false; } case TypeTableEntryIdMetaType: return a->data.x_type == b->data.x_type; case TypeTableEntryIdVoid: return true; case TypeTableEntryIdPureError: return a->data.x_pure_err == b->data.x_pure_err; case TypeTableEntryIdFn: return a->data.x_fn == b->data.x_fn; case TypeTableEntryIdBool: return a->data.x_bool == b->data.x_bool; case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdEnumTag: return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum); case TypeTableEntryIdPointer: 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) 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) 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; } zig_unreachable(); 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 TypeTableEntryIdUnion: zig_panic("TODO"); case TypeTableEntryIdUndefLit: zig_panic("TODO"); case TypeTableEntryIdNullLit: zig_panic("TODO"); case TypeTableEntryIdMaybe: zig_panic("TODO"); case TypeTableEntryIdErrorUnion: zig_panic("TODO"); case TypeTableEntryIdTypeDecl: 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 TypeTableEntryIdVar: zig_unreachable(); } zig_unreachable(); } uint64_t max_unsigned_val(TypeTableEntry *type_entry) { assert(type_entry->id == TypeTableEntryIdInt); if (type_entry->data.integral.bit_count == 64) { return UINT64_MAX; } else { return (((uint64_t)1) << type_entry->data.integral.bit_count) - 1; } } static int64_t max_signed_val(TypeTableEntry *type_entry) { assert(type_entry->id == TypeTableEntryIdInt); if (type_entry->data.integral.bit_count == 64) { return INT64_MAX; } else { return (((uint64_t)1) << (type_entry->data.integral.bit_count - 1)) - 1; } } int64_t min_signed_val(TypeTableEntry *type_entry) { assert(type_entry->id == TypeTableEntryIdInt); if (type_entry->data.integral.bit_count == 64) { return INT64_MIN; } else { return -((int64_t)(((uint64_t)1) << (type_entry->data.integral.bit_count - 1))); } } 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; if (is_max) { if (type_entry->data.integral.is_signed) { int64_t val = max_signed_val(type_entry); bignum_init_signed(&const_val->data.x_bignum, val); } else { uint64_t val = max_unsigned_val(type_entry); bignum_init_unsigned(&const_val->data.x_bignum, val); } } else { if (type_entry->data.integral.is_signed) { int64_t val = min_signed_val(type_entry); bignum_init_signed(&const_val->data.x_bignum, val); } else { bignum_init_unsigned(&const_val->data.x_bignum, 0); } } } 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_value(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 *canon_type = get_underlying_type(const_val->type); switch (canon_type->id) { case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdInvalid: buf_appendf(buf, "(invalid)"); return; case TypeTableEntryIdVar: buf_appendf(buf, "(var)"); return; case TypeTableEntryIdVoid: buf_appendf(buf, "{}"); return; case TypeTableEntryIdNumLitFloat: buf_appendf(buf, "%f", const_val->data.x_bignum.data.x_float); return; case TypeTableEntryIdNumLitInt: { BigNum *bignum = &const_val->data.x_bignum; const char *negative_str = bignum->is_negative ? "-" : ""; buf_appendf(buf, "%s%llu", negative_str, bignum->data.x_uint); return; } case TypeTableEntryIdMetaType: buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name)); return; case TypeTableEntryIdInt: { BigNum *bignum = &const_val->data.x_bignum; assert(bignum->kind == BigNumKindInt); const char *negative_str = bignum->is_negative ? "-" : ""; buf_appendf(buf, "%s%llu", negative_str, bignum->data.x_uint); } return; case TypeTableEntryIdFloat: { BigNum *bignum = &const_val->data.x_bignum; assert(bignum->kind == BigNumKindFloat); buf_appendf(buf, "%f", bignum->data.x_float); } 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 TypeTableEntryIdPointer: switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); case ConstPtrSpecialRef: case ConstPtrSpecialBaseStruct: buf_appendf(buf, "&"); render_const_value(buf, const_ptr_pointee(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(buf, const_ptr_pointee(const_val)); return; } case ConstPtrSpecialHardCodedAddr: buf_appendf(buf, "(&%s)(%" PRIx64 ")", buf_ptr(&canon_type->data.pointer.child_type->name), const_val->data.x_ptr.data.hard_coded_addr.addr); return; case ConstPtrSpecialDiscard: buf_append_str(buf, "&_"); return; } zig_unreachable(); case TypeTableEntryIdFn: { FnTableEntry *fn_entry = const_val->data.x_fn; buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name)); return; } case TypeTableEntryIdBlock: { AstNode *node = const_val->data.x_block->source_node; buf_appendf(buf, "(scope:%zu:%zu)", node->line + 1, node->column + 1); return; } case TypeTableEntryIdArray: { TypeTableEntry *child_type = canon_type->data.array.child_type; uint64_t len = canon_type->data.array.len; // 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.elements[i]; uint64_t big_c = child_value->data.x_bignum.data.x_uint; assert(big_c <= UINT8_MAX); uint8_t c = 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(&canon_type->name)); for (uint64_t i = 0; i < len; i += 1) { if (i != 0) buf_appendf(buf, ","); ConstExprValue *child_value = &const_val->data.x_array.elements[i]; render_const_value(buf, child_value); } buf_appendf(buf, "}"); return; } case TypeTableEntryIdNullLit: { buf_appendf(buf, "null"); return; } case TypeTableEntryIdUndefLit: { buf_appendf(buf, "undefined"); return; } case TypeTableEntryIdMaybe: { if (const_val->data.x_maybe) { render_const_value(buf, const_val->data.x_maybe); } 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(&canon_type->name)); return; } case TypeTableEntryIdEnum: { buf_appendf(buf, "(enum %s constant)", buf_ptr(&canon_type->name)); return; } case TypeTableEntryIdErrorUnion: { buf_appendf(buf, "(error union %s constant)", buf_ptr(&canon_type->name)); return; } case TypeTableEntryIdUnion: { buf_appendf(buf, "(union %s constant)", buf_ptr(&canon_type->name)); return; } case TypeTableEntryIdPureError: { buf_appendf(buf, "(pure error constant)"); return; } case TypeTableEntryIdEnumTag: { TypeTableEntry *enum_type = canon_type->data.enum_tag.enum_type; TypeEnumField *field = &enum_type->data.enumeration.fields[const_val->data.x_bignum.data.x_uint]; buf_appendf(buf, "%s.%s", buf_ptr(&enum_type->name), buf_ptr(field->name)); return; } case TypeTableEntryIdArgTuple: { buf_appendf(buf, "(args value)"); return; } } zig_unreachable(); } TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, size_t size_in_bits) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->type_ref = LLVMIntType(size_in_bits); const char u_or_i = is_signed ? 'i' : 'u'; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "%c%zu", 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 = 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 TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: zig_unreachable(); case TypeTableEntryIdPointer: return hash_ptr(x.data.pointer.child_type) + (x.data.pointer.is_const ? 2749109194 : 4047371087) + (x.data.pointer.is_volatile ? 536730450 : 1685612214) + (((uint32_t)x.data.pointer.bit_offset) * 2639019452) + (((uint32_t)x.data.pointer.unaligned_bit_count) * 529908881); case TypeTableEntryIdArray: return hash_ptr(x.data.array.child_type) + (x.data.array.size * 2122979968); case TypeTableEntryIdInt: return (x.data.integer.is_signed ? 2652528194 : 163929201) + (((uint32_t)x.data.integer.bit_count) * 2998081557); } zig_unreachable(); } bool type_id_eql(TypeId a, TypeId b) { if (a.id != b.id) return false; switch (a.id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: zig_unreachable(); case TypeTableEntryIdPointer: return a.data.pointer.child_type == b.data.pointer.child_type && a.data.pointer.is_const == b.data.pointer.is_const && a.data.pointer.is_volatile == b.data.pointer.is_volatile && 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 x.data.ctz.bit_count * 810453934; case ZigLLVMFnIdClz: return x.data.clz.bit_count * 2428952817; case ZigLLVMFnIdOverflowArithmetic: return (x.data.overflow_arithmetic.bit_count * 87135777) + (x.data.overflow_arithmetic.add_sub_mul * 31640542) + (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 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(); } ConstParent *get_const_val_parent(ConstExprValue *value) { assert(value->type); TypeTableEntry *canon_type = get_underlying_type(value->type); if (canon_type->id == TypeTableEntryIdArray) { return &value->data.x_array.parent; } else if (canon_type->id == TypeTableEntryIdStruct) { return &value->data.x_struct.parent; } return nullptr; }