From 528832bd3a2e7b686ee84aef5887df740a6114db Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Sep 2020 18:38:55 -0700 Subject: rename src-self-hosted/ to src/ --- src/stage1/analyze.cpp | 9941 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 9941 insertions(+) create mode 100644 src/stage1/analyze.cpp (limited to 'src/stage1/analyze.cpp') diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp new file mode 100644 index 0000000000..6c6f198e7a --- /dev/null +++ b/src/stage1/analyze.cpp @@ -0,0 +1,9941 @@ +/* + * 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 "codegen.hpp" +#include "config.h" +#include "error.hpp" +#include "ir.hpp" +#include "ir_print.hpp" +#include "os.hpp" +#include "parser.hpp" +#include "softfloat.hpp" +#include "zig_llvm.h" + + +static const size_t default_backward_branch_quota = 1000; + +static Error ATTRIBUTE_MUST_USE resolve_struct_type(CodeGen *g, ZigType *struct_type); + +static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type); +static Error ATTRIBUTE_MUST_USE resolve_struct_alignment(CodeGen *g, ZigType *struct_type); +static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type); +static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type); +static Error ATTRIBUTE_MUST_USE resolve_union_alignment(CodeGen *g, ZigType *union_type); +static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); +static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status); +static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope); +static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope); +static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame); + +// nullptr means not analyzed yet; this one means currently being analyzed +static const AstNode *inferred_async_checking = reinterpret_cast(0x1); +// this one means analyzed and it's not async +static const AstNode *inferred_async_none = reinterpret_cast(0x2); + +static bool is_top_level_struct(ZigType *import) { + return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr; +} + +static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ZigType *owner, Token *token, Buf *msg) { + assert(is_top_level_struct(owner)); + RootStruct *root_struct = owner->data.structure.root_struct; + + ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column, + root_struct->source_code, root_struct->line_offsets, msg); + + err_msg_add_note(parent_msg, err); + return err; +} + +ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg) { + assert(is_top_level_struct(owner)); + RootStruct *root_struct = owner->data.structure.root_struct; + ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column, + root_struct->source_code, root_struct->line_offsets, msg); + + g->errors.append(err); + g->trace_err = err; + return err; +} + +ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { + Token fake_token; + fake_token.start_line = node->line; + fake_token.start_column = node->column; + node->already_traced_this_node = true; + return add_token_error(g, node->owner, &fake_token, msg); +} + +ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, Buf *msg) { + Token fake_token; + fake_token.start_line = node->line; + fake_token.start_column = node->column; + return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg); +} + +ZigType *new_type_table_entry(ZigTypeId id) { + ZigType *entry = heap::c_allocator.create(); + entry->id = id; + return entry; +} + +static ScopeDecls **get_container_scope_ptr(ZigType *type_entry) { + if (type_entry->id == ZigTypeIdStruct) { + return &type_entry->data.structure.decls_scope; + } else if (type_entry->id == ZigTypeIdEnum) { + return &type_entry->data.enumeration.decls_scope; + } else if (type_entry->id == ZigTypeIdUnion) { + return &type_entry->data.unionation.decls_scope; + } + zig_unreachable(); +} + +static ScopeExpr *find_expr_scope(Scope *scope) { + for (;;) { + switch (scope->id) { + case ScopeIdExpr: + return reinterpret_cast(scope); + case ScopeIdDefer: + case ScopeIdDeferExpr: + case ScopeIdDecls: + case ScopeIdFnDef: + case ScopeIdCompTime: + case ScopeIdNoSuspend: + case ScopeIdVarDecl: + case ScopeIdCImport: + case ScopeIdSuspend: + case ScopeIdTypeOf: + case ScopeIdBlock: + return nullptr; + case ScopeIdLoop: + case ScopeIdRuntime: + scope = scope->parent; + continue; + } + } +} + +static void update_progress_display(CodeGen *g) { + stage2_progress_update_node(g->sub_progress_node, + g->resolve_queue_index + g->fn_defs_index, + g->resolve_queue.length + g->fn_defs.length); +} + +ScopeDecls *get_container_scope(ZigType *type_entry) { + return *get_container_scope_ptr(type_entry); +} + +void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) { + dest->codegen = g; + dest->id = id; + dest->source_node = source_node; + dest->parent = parent; +} + +ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, + ZigType *import, Buf *bare_name) +{ + ScopeDecls *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdDecls, node, parent); + scope->decl_table.init(4); + scope->container_type = container_type; + scope->import = import; + scope->bare_name = bare_name; + return scope; +} + +ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent) { + assert(node->type == NodeTypeBlock); + ScopeBlock *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdBlock, node, parent); + scope->name = node->data.block.name; + return scope; +} + +ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent) { + assert(node->type == NodeTypeDefer); + ScopeDefer *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdDefer, node, parent); + return scope; +} + +ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent) { + assert(node->type == NodeTypeDefer); + ScopeDeferExpr *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdDeferExpr, node, parent); + return scope; +} + +Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) { + ScopeVarDecl *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdVarDecl, node, parent); + scope->var = var; + return &scope->base; +} + +ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent) { + assert(node->type == NodeTypeFnCallExpr); + ScopeCImport *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdCImport, node, parent); + buf_resize(&scope->buf, 0); + return scope; +} + +ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent) { + ScopeLoop *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdLoop, node, parent); + if (node->type == NodeTypeWhileExpr) { + scope->name = node->data.while_expr.name; + } else if (node->type == NodeTypeForExpr) { + scope->name = node->data.for_expr.name; + } else { + zig_unreachable(); + } + return scope; +} + +Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime) { + ScopeRuntime *scope = heap::c_allocator.create(); + scope->is_comptime = is_comptime; + init_scope(g, &scope->base, ScopeIdRuntime, node, parent); + return &scope->base; +} + +ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent) { + assert(node->type == NodeTypeSuspend); + ScopeSuspend *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdSuspend, node, parent); + return scope; +} + +ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry) { + ScopeFnDef *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdFnDef, node, parent); + scope->fn_entry = fn_entry; + return scope; +} + +Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) { + ScopeCompTime *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdCompTime, node, parent); + return &scope->base; +} + +Scope *create_nosuspend_scope(CodeGen *g, AstNode *node, Scope *parent) { + ScopeNoSuspend *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdNoSuspend, node, parent); + return &scope->base; +} + +Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) { + ScopeTypeOf *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdTypeOf, node, parent); + return &scope->base; +} + +ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent) { + ScopeExpr *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdExpr, node, parent); + ScopeExpr *parent_expr = find_expr_scope(parent); + if (parent_expr != nullptr) { + size_t new_len = parent_expr->children_len + 1; + parent_expr->children_ptr = heap::c_allocator.reallocate_nonzero( + parent_expr->children_ptr, parent_expr->children_len, new_len); + parent_expr->children_ptr[parent_expr->children_len] = scope; + parent_expr->children_len = new_len; + } + return scope; +} + +ZigType *get_scope_import(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + assert(is_top_level_struct(decls_scope->import)); + return decls_scope->import; + } + scope = scope->parent; + } + zig_unreachable(); +} + +ScopeTypeOf *get_scope_typeof(Scope *scope) { + while (scope) { + switch (scope->id) { + case ScopeIdTypeOf: + return reinterpret_cast(scope); + case ScopeIdFnDef: + case ScopeIdDecls: + return nullptr; + default: + scope = scope->parent; + continue; + } + } + zig_unreachable(); +} + +static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope, + Buf *bare_name) +{ + ZigType *entry = new_type_table_entry(id); + *get_container_scope_ptr(entry) = create_decls_scope(g, source_node, parent_scope, entry, + get_scope_import(parent_scope), bare_name); + return entry; +} + +static uint8_t bits_needed_for_unsigned(uint64_t x) { + if (x == 0) { + return 0; + } + uint8_t base = log2_u64(x); + uint64_t upper = (((uint64_t)1) << base) - 1; + return (upper >= x) ? base : (base + 1); +} + +AstNode *type_decl_node(ZigType *type_entry) { + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdStruct: + return type_entry->data.structure.decl_node; + case ZigTypeIdEnum: + return type_entry->data.enumeration.decl_node; + case ZigTypeIdUnion: + return type_entry->data.unionation.decl_node; + case ZigTypeIdFnFrame: + return type_entry->data.frame.fn->proto_node; + case ZigTypeIdOpaque: + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdVector: + case ZigTypeIdAnyFrame: + return nullptr; + } + zig_unreachable(); +} + +bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdStruct: + return type_entry->data.structure.resolve_status >= status; + case ZigTypeIdUnion: + return type_entry->data.unionation.resolve_status >= status; + case ZigTypeIdEnum: + return type_entry->data.enumeration.resolve_status >= status; + case ZigTypeIdFnFrame: + switch (status) { + case ResolveStatusInvalid: + zig_unreachable(); + case ResolveStatusBeingInferred: + zig_unreachable(); + case ResolveStatusUnstarted: + case ResolveStatusZeroBitsKnown: + return true; + case ResolveStatusAlignmentKnown: + case ResolveStatusSizeKnown: + return type_entry->data.frame.locals_struct != nullptr; + case ResolveStatusLLVMFwdDecl: + case ResolveStatusLLVMFull: + return type_entry->llvm_type != nullptr; + } + zig_unreachable(); + case ZigTypeIdOpaque: + return status < ResolveStatusSizeKnown; + case ZigTypeIdPointer: + switch (status) { + case ResolveStatusInvalid: + zig_unreachable(); + case ResolveStatusBeingInferred: + zig_unreachable(); + case ResolveStatusUnstarted: + return true; + case ResolveStatusZeroBitsKnown: + case ResolveStatusAlignmentKnown: + case ResolveStatusSizeKnown: + return type_entry->abi_size != SIZE_MAX; + case ResolveStatusLLVMFwdDecl: + case ResolveStatusLLVMFull: + return type_entry->llvm_type != nullptr; + } + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdArray: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdVector: + case ZigTypeIdAnyFrame: + return true; + } + zig_unreachable(); +} + +bool type_is_complete(ZigType *type_entry) { + return type_is_resolved(type_entry, ResolveStatusSizeKnown); +} + +uint64_t type_size(CodeGen *g, ZigType *type_entry) { + assert(type_is_resolved(type_entry, ResolveStatusSizeKnown)); + return type_entry->abi_size; +} + +uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) { + assert(type_is_resolved(type_entry, ResolveStatusSizeKnown)); + return type_entry->size_in_bits; +} + +uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) { + assert(type_is_resolved(type_entry, ResolveStatusAlignmentKnown)); + return type_entry->abi_align; +} + +static bool is_slice(ZigType *type) { + return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; +} + +ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { + return get_int_type(g, false, bits_needed_for_unsigned(x)); +} + +ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type) { + if (result_type != nullptr && result_type->any_frame_parent != nullptr) { + return result_type->any_frame_parent; + } else if (result_type == nullptr && g->builtin_types.entry_any_frame != nullptr) { + return g->builtin_types.entry_any_frame; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdAnyFrame); + entry->abi_size = g->builtin_types.entry_usize->abi_size; + entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits; + entry->abi_align = g->builtin_types.entry_usize->abi_align; + entry->data.any_frame.result_type = result_type; + buf_init_from_str(&entry->name, "anyframe"); + if (result_type != nullptr) { + buf_appendf(&entry->name, "->%s", buf_ptr(&result_type->name)); + } + + if (result_type != nullptr) { + result_type->any_frame_parent = entry; + } else if (result_type == nullptr) { + g->builtin_types.entry_any_frame = entry; + } + return entry; +} + +ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) { + if (fn->frame_type != nullptr) { + return fn->frame_type; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdFnFrame); + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "@Frame(%s)", buf_ptr(&fn->symbol_name)); + + entry->data.frame.fn = fn; + + // Async function frames are always non-zero bits because they always have a resume index. + entry->abi_size = SIZE_MAX; + entry->size_in_bits = SIZE_MAX; + + fn->frame_type = entry; + return entry; +} + +static void append_ptr_type_attrs(Buf *type_name, ZigType *ptr_type) { + const char *const_str = ptr_type->data.pointer.is_const ? "const " : ""; + const char *volatile_str = ptr_type->data.pointer.is_volatile ? "volatile " : ""; + const char *allow_zero_str; + if (ptr_type->data.pointer.ptr_len == PtrLenC) { + assert(ptr_type->data.pointer.allow_zero); + allow_zero_str = ""; + } else { + allow_zero_str = ptr_type->data.pointer.allow_zero ? "allowzero " : ""; + } + if (ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.host_int_bytes != 0 || + ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) + { + buf_appendf(type_name, "align("); + if (ptr_type->data.pointer.explicit_alignment != 0) { + buf_appendf(type_name, "%" PRIu32, ptr_type->data.pointer.explicit_alignment); + } + if (ptr_type->data.pointer.host_int_bytes != 0) { + buf_appendf(type_name, ":%" PRIu32 ":%" PRIu32, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + } + if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { + buf_appendf(type_name, ":?"); + } else if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { + buf_appendf(type_name, ":%" PRIu32, ptr_type->data.pointer.vector_index); + } + buf_appendf(type_name, ") "); + } + buf_appendf(type_name, "%s%s%s", const_str, volatile_str, allow_zero_str); + if (ptr_type->data.pointer.inferred_struct_field != nullptr) { + buf_appendf(type_name, " field '%s' of %s)", + buf_ptr(ptr_type->data.pointer.inferred_struct_field->field_name), + buf_ptr(&ptr_type->data.pointer.inferred_struct_field->inferred_struct_type->name)); + } else { + buf_appendf(type_name, "%s", buf_ptr(&ptr_type->data.pointer.child_type->name)); + } +} + +ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_const, + bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, + uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero, + uint32_t vector_index, InferredStructField *inferred_struct_field, ZigValue *sentinel) +{ + assert(ptr_len != PtrLenC || allow_zero); + assert(!type_is_invalid(child_type)); + assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque); + + if (byte_alignment != 0) { + uint32_t abi_alignment = get_abi_alignment(g, child_type); + if (byte_alignment == abi_alignment) + byte_alignment = 0; + } + + if (host_int_bytes != 0 && vector_index == VECTOR_INDEX_NONE) { + uint32_t child_type_bits = type_size_bits(g, child_type); + if (host_int_bytes * 8 == child_type_bits) { + assert(bit_offset_in_host == 0); + host_int_bytes = 0; + } + } + + TypeId type_id = {}; + ZigType **parent_pointer = nullptr; + if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || + allow_zero || vector_index != VECTOR_INDEX_NONE || inferred_struct_field != nullptr || + sentinel != nullptr) + { + type_id.id = ZigTypeIdPointer; + type_id.data.pointer.codegen = g; + type_id.data.pointer.child_type = child_type; + type_id.data.pointer.is_const = is_const; + type_id.data.pointer.is_volatile = is_volatile; + type_id.data.pointer.alignment = byte_alignment; + type_id.data.pointer.bit_offset_in_host = bit_offset_in_host; + type_id.data.pointer.host_int_bytes = host_int_bytes; + type_id.data.pointer.ptr_len = ptr_len; + type_id.data.pointer.allow_zero = allow_zero; + type_id.data.pointer.vector_index = vector_index; + type_id.data.pointer.inferred_struct_field = inferred_struct_field; + type_id.data.pointer.sentinel = sentinel; + + auto existing_entry = g->type_table.maybe_get(type_id); + if (existing_entry) + return existing_entry->value; + } else { + assert(bit_offset_in_host == 0); + parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)]; + if (*parent_pointer) { + assert((*parent_pointer)->data.pointer.explicit_alignment == 0); + return *parent_pointer; + } + } + + ZigType *entry = new_type_table_entry(ZigTypeIdPointer); + + buf_resize(&entry->name, 0); + if (inferred_struct_field != nullptr) { + buf_appendf(&entry->name, "("); + } + switch (ptr_len) { + case PtrLenSingle: + assert(sentinel == nullptr); + buf_appendf(&entry->name, "*"); + break; + case PtrLenUnknown: + buf_appendf(&entry->name, "[*"); + break; + case PtrLenC: + assert(sentinel == nullptr); + buf_appendf(&entry->name, "[*c]"); + break; + } + if (sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, sentinel); + } + switch (ptr_len) { + case PtrLenSingle: + case PtrLenC: + break; + case PtrLenUnknown: + buf_appendf(&entry->name, "]"); + break; + } + + if (inferred_struct_field != nullptr) { + entry->abi_size = SIZE_MAX; + entry->size_in_bits = SIZE_MAX; + entry->abi_align = UINT32_MAX; + } else if (type_is_resolved(child_type, ResolveStatusZeroBitsKnown)) { + if (type_has_bits(g, child_type)) { + entry->abi_size = g->builtin_types.entry_usize->abi_size; + entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits; + entry->abi_align = g->builtin_types.entry_usize->abi_align; + } else { + assert(byte_alignment == 0); + entry->abi_size = 0; + entry->size_in_bits = 0; + entry->abi_align = 0; + } + } else { + entry->abi_size = SIZE_MAX; + entry->size_in_bits = SIZE_MAX; + entry->abi_align = UINT32_MAX; + } + + entry->data.pointer.ptr_len = ptr_len; + entry->data.pointer.child_type = child_type; + entry->data.pointer.is_const = is_const; + entry->data.pointer.is_volatile = is_volatile; + entry->data.pointer.explicit_alignment = byte_alignment; + entry->data.pointer.bit_offset_in_host = bit_offset_in_host; + entry->data.pointer.host_int_bytes = host_int_bytes; + entry->data.pointer.allow_zero = allow_zero; + entry->data.pointer.vector_index = vector_index; + entry->data.pointer.inferred_struct_field = inferred_struct_field; + entry->data.pointer.sentinel = sentinel; + + append_ptr_type_attrs(&entry->name, entry); + + if (parent_pointer) { + *parent_pointer = entry; + } else { + g->type_table.put(type_id, entry); + } + return entry; +} + +ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, + bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, + uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero) +{ + return get_pointer_to_type_extra2(g, child_type, is_const, is_volatile, ptr_len, + byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr, nullptr); +} + +ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) { + return get_pointer_to_type_extra2(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, nullptr); +} + +ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { + ZigType *result = get_optional_type2(g, child_type); + if (result == nullptr) { + codegen_report_errors_and_exit(g); + } + return result; +} + +ZigType *get_optional_type2(CodeGen *g, ZigType *child_type) { + if (child_type->optional_parent != nullptr) { + return child_type->optional_parent; + } + + Error err; + if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { + return nullptr; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdOptional); + + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); + + if (!type_has_bits(g, child_type)) { + entry->size_in_bits = g->builtin_types.entry_bool->size_in_bits; + entry->abi_size = g->builtin_types.entry_bool->abi_size; + entry->abi_align = g->builtin_types.entry_bool->abi_align; + } else if (type_is_nonnull_ptr(g, child_type) || child_type->id == ZigTypeIdErrorSet) { + // This is an optimization but also is necessary for calling C + // functions where all pointers are optional pointers. + // Function types are technically pointers. + entry->size_in_bits = child_type->size_in_bits; + entry->abi_size = child_type->abi_size; + entry->abi_align = child_type->abi_align; + } else { + // This value only matters if the type is legal in a packed struct, which is not + // true for optional types which did not fit the above 2 categories (zero bit child type, + // or nonnull ptr child type, or error set child type). + entry->size_in_bits = child_type->size_in_bits + 1; + + // We're going to make a struct with the child type as the first field, + // and a bool as the second. Since the child type's abi alignment is guaranteed + // to be >= the bool's abi size (1 byte), the added size is exactly equal to the + // child type's ABI alignment. + assert(child_type->abi_align >= g->builtin_types.entry_bool->abi_size); + entry->abi_align = child_type->abi_align; + entry->abi_size = child_type->abi_size + child_type->abi_align; + } + + entry->data.maybe.child_type = child_type; + entry->data.maybe.resolve_status = ResolveStatusSizeKnown; + + child_type->optional_parent = entry; + return entry; +} + +static size_t align_forward(size_t addr, size_t alignment) { + return (addr + alignment - 1) & ~(alignment - 1); +} + +static size_t next_field_offset(size_t offset, size_t align_from_zero, size_t field_size, size_t next_field_align) { + // Convert offset to a pretend address which has the specified alignment. + size_t addr = offset + align_from_zero; + // March the address forward to respect the field alignment. + size_t aligned_addr = align_forward(addr + field_size, next_field_align); + // Convert back from pretend address to offset. + return aligned_addr - align_from_zero; +} + +ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type) { + assert(err_set_type->id == ZigTypeIdErrorSet); + assert(!type_is_invalid(payload_type)); + + TypeId type_id = {}; + type_id.id = ZigTypeIdErrorUnion; + type_id.data.error_union.err_set_type = err_set_type; + type_id.data.error_union.payload_type = payload_type; + + auto existing_entry = g->type_table.maybe_get(type_id); + if (existing_entry) { + return existing_entry->value; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdErrorUnion); + assert(type_is_resolved(payload_type, ResolveStatusSizeKnown)); + + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name)); + + entry->data.error_union.err_set_type = err_set_type; + entry->data.error_union.payload_type = payload_type; + + if (!type_has_bits(g, payload_type)) { + if (type_has_bits(g, err_set_type)) { + entry->size_in_bits = err_set_type->size_in_bits; + entry->abi_size = err_set_type->abi_size; + entry->abi_align = err_set_type->abi_align; + } else { + entry->size_in_bits = 0; + entry->abi_size = 0; + entry->abi_align = 0; + } + } else if (!type_has_bits(g, err_set_type)) { + entry->size_in_bits = payload_type->size_in_bits; + entry->abi_size = payload_type->abi_size; + entry->abi_align = payload_type->abi_align; + } else { + entry->abi_align = max(err_set_type->abi_align, payload_type->abi_align); + size_t field_sizes[2]; + size_t field_aligns[2]; + field_sizes[err_union_err_index] = err_set_type->abi_size; + field_aligns[err_union_err_index] = err_set_type->abi_align; + field_sizes[err_union_payload_index] = payload_type->abi_size; + field_aligns[err_union_payload_index] = payload_type->abi_align; + size_t field2_offset = next_field_offset(0, entry->abi_align, field_sizes[0], field_aligns[1]); + entry->abi_size = next_field_offset(field2_offset, entry->abi_align, field_sizes[1], entry->abi_align); + entry->size_in_bits = entry->abi_size * 8; + entry->data.error_union.pad_bytes = entry->abi_size - (field2_offset + field_sizes[1]); + } + + g->type_table.put(type_id, entry); + return entry; +} + +ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) { + Error err; + + TypeId type_id = {}; + type_id.id = ZigTypeIdArray; + type_id.data.array.codegen = g; + type_id.data.array.child_type = child_type; + type_id.data.array.size = array_size; + type_id.data.array.sentinel = sentinel; + auto existing_entry = g->type_table.maybe_get(type_id); + if (existing_entry) { + return existing_entry->value; + } + + size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0); + + if (full_array_size != 0 && (err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { + codegen_report_errors_and_exit(g); + } + + ZigType *entry = new_type_table_entry(ZigTypeIdArray); + + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "[%" ZIG_PRI_u64, array_size); + if (sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, sentinel); + } + buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name)); + + entry->size_in_bits = child_type->size_in_bits * full_array_size; + entry->abi_align = (full_array_size == 0) ? 0 : child_type->abi_align; + entry->abi_size = child_type->abi_size * full_array_size; + + entry->data.array.child_type = child_type; + entry->data.array.len = array_size; + entry->data.array.sentinel = sentinel; + + g->type_table.put(type_id, entry); + return entry; +} + +ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { + Error err; + assert(ptr_type->id == ZigTypeIdPointer); + assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown); + + ZigType **parent_pointer = &ptr_type->data.pointer.slice_parent; + if (*parent_pointer) { + return *parent_pointer; + } + + // We use the pointer type's abi size below, so we have to resolve it now. + if ((err = type_resolve(g, ptr_type, ResolveStatusSizeKnown))) { + codegen_report_errors_and_exit(g); + } + + ZigType *entry = new_type_table_entry(ZigTypeIdStruct); + + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "["); + if (ptr_type->data.pointer.sentinel != nullptr) { + buf_appendf(&entry->name, ":"); + render_const_value(g, &entry->name, ptr_type->data.pointer.sentinel); + } + buf_appendf(&entry->name, "]"); + append_ptr_type_attrs(&entry->name, ptr_type); + + unsigned element_count = 2; + Buf *ptr_field_name = buf_create_from_str("ptr"); + Buf *len_field_name = buf_create_from_str("len"); + + entry->data.structure.resolve_status = ResolveStatusSizeKnown; + entry->data.structure.layout = ContainerLayoutAuto; + entry->data.structure.special = StructSpecialSlice; + entry->data.structure.src_field_count = element_count; + entry->data.structure.gen_field_count = element_count; + entry->data.structure.fields = alloc_type_struct_fields(element_count); + entry->data.structure.fields_by_name.init(element_count); + entry->data.structure.fields[slice_ptr_index]->name = ptr_field_name; + entry->data.structure.fields[slice_ptr_index]->type_entry = ptr_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_ptr_index]->offset = 0; + entry->data.structure.fields[slice_len_index]->name = len_field_name; + entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize; + entry->data.structure.fields[slice_len_index]->src_index = slice_len_index; + entry->data.structure.fields[slice_len_index]->gen_index = 1; + entry->data.structure.fields[slice_len_index]->offset = ptr_type->abi_size; + + entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]); + entry->data.structure.fields_by_name.put(len_field_name, entry->data.structure.fields[slice_len_index]); + + switch (type_requires_comptime(g, ptr_type)) { + case ReqCompTimeInvalid: + zig_unreachable(); + case ReqCompTimeNo: + break; + case ReqCompTimeYes: + entry->data.structure.requires_comptime = true; + } + + if (!type_has_bits(g, ptr_type)) { + 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; + } + + if (type_has_bits(g, ptr_type)) { + entry->size_in_bits = ptr_type->size_in_bits + g->builtin_types.entry_usize->size_in_bits; + entry->abi_size = ptr_type->abi_size + g->builtin_types.entry_usize->abi_size; + entry->abi_align = ptr_type->abi_align; + } else { + entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits; + entry->abi_size = g->builtin_types.entry_usize->abi_size; + entry->abi_align = g->builtin_types.entry_usize->abi_align; + } + + *parent_pointer = entry; + return entry; +} + +ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name) { + ZigType *entry = new_type_table_entry(ZigTypeIdOpaque); + + buf_init_from_str(&entry->name, full_name); + + ZigType *import = scope ? get_scope_import(scope) : nullptr; + unsigned line = source_node ? (unsigned)(source_node->line + 1) : 0; + + entry->llvm_type = LLVMInt8Type(); + entry->llvm_di_type = ZigLLVMCreateDebugForwardDeclType(g->dbuilder, + ZigLLVMTag_DW_structure_type(), full_name, + import ? ZigLLVMFileToScope(import->data.structure.root_struct->di_file) : nullptr, + import ? import->data.structure.root_struct->di_file : nullptr, + line); + entry->data.opaque.bare_name = bare_name; + + // The actual size is unknown, but the value must not be 0 because that + // is how type_has_bits is determined. + entry->abi_size = SIZE_MAX; + entry->size_in_bits = SIZE_MAX; + entry->abi_align = 1; + + return entry; +} + +ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) { + ZigType *fn_type = fn_entry->type_entry; + assert(fn_type->id == ZigTypeIdFn); + if (fn_type->data.fn.bound_fn_parent) + return fn_type->data.fn.bound_fn_parent; + + ZigType *bound_fn_type = new_type_table_entry(ZigTypeIdBoundFn); + bound_fn_type->data.bound_fn.fn_type = fn_type; + + 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; +} + +const char *calling_convention_name(CallingConvention cc) { + switch (cc) { + case CallingConventionUnspecified: return "Unspecified"; + case CallingConventionC: return "C"; + case CallingConventionCold: return "Cold"; + case CallingConventionNaked: return "Naked"; + case CallingConventionAsync: return "Async"; + case CallingConventionInterrupt: return "Interrupt"; + case CallingConventionSignal: return "Signal"; + case CallingConventionStdcall: return "Stdcall"; + case CallingConventionFastcall: return "Fastcall"; + case CallingConventionVectorcall: return "Vectorcall"; + case CallingConventionThiscall: return "Thiscall"; + case CallingConventionAPCS: return "Apcs"; + case CallingConventionAAPCS: return "Aapcs"; + case CallingConventionAAPCSVFP: return "Aapcsvfp"; + } + zig_unreachable(); +} + +bool calling_convention_allows_zig_types(CallingConvention cc) { + switch (cc) { + case CallingConventionUnspecified: + case CallingConventionAsync: + return true; + case CallingConventionC: + case CallingConventionCold: + case CallingConventionNaked: + case CallingConventionInterrupt: + case CallingConventionSignal: + case CallingConventionStdcall: + case CallingConventionFastcall: + case CallingConventionVectorcall: + case CallingConventionThiscall: + case CallingConventionAPCS: + case CallingConventionAAPCS: + case CallingConventionAAPCSVFP: + return false; + } + zig_unreachable(); +} + +ZigType *get_stack_trace_type(CodeGen *g) { + if (g->stack_trace_type == nullptr) { + g->stack_trace_type = get_builtin_type(g, "StackTrace"); + assertNoError(type_resolve(g, g->stack_trace_type, ResolveStatusZeroBitsKnown)); + } + return g->stack_trace_type; +} + +bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { + if (fn_type_id->cc == CallingConventionUnspecified) { + return handle_is_ptr(g, fn_type_id->return_type); + } + if (fn_type_id->cc != CallingConventionC) { + return false; + } + if (type_is_c_abi_int_bail(g, fn_type_id->return_type)) { + return false; + } + if (g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64 || + target_is_arm(g->zig_target) || + target_is_riscv(g->zig_target) || + target_is_wasm(g->zig_target) || + target_is_ppc(g->zig_target)) + { + X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); + return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval; + } else if (g->zig_target->arch == ZigLLVM_mips || g->zig_target->arch == ZigLLVM_mipsel) { + return false; + } + zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481"); +} + +ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { + Error err; + auto table_entry = g->fn_type_table.maybe_get(fn_type_id); + if (table_entry) { + return table_entry->value; + } + if (fn_type_id->return_type != nullptr) { + if ((err = type_resolve(g, fn_type_id->return_type, ResolveStatusSizeKnown))) + return g->builtin_types.entry_invalid; + assert(fn_type_id->return_type->id != ZigTypeIdOpaque); + } else { + zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"); + } + + ZigType *fn_type = new_type_table_entry(ZigTypeIdFn); + fn_type->data.fn.fn_type_id = *fn_type_id; + + // populate the name of the type + buf_resize(&fn_type->name, 0); + buf_appendf(&fn_type->name, "fn("); + for (size_t i = 0; i < fn_type_id->param_count; i += 1) { + FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; + + ZigType *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)); + } + + if (fn_type_id->is_var_args) { + const char *comma = (fn_type_id->param_count == 0) ? "" : ", "; + buf_appendf(&fn_type->name, "%s...", comma); + } + buf_appendf(&fn_type->name, ")"); + if (fn_type_id->alignment != 0) { + buf_appendf(&fn_type->name, " align(%" PRIu32 ")", fn_type_id->alignment); + } + if (fn_type_id->cc != CallingConventionUnspecified) { + buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc)); + } + buf_appendf(&fn_type->name, " %s", buf_ptr(&fn_type_id->return_type->name)); + + // The fn_type is a pointer; not to be confused with the raw function type. + fn_type->size_in_bits = g->builtin_types.entry_usize->size_in_bits; + fn_type->abi_size = g->builtin_types.entry_usize->abi_size; + fn_type->abi_align = g->builtin_types.entry_usize->abi_align; + + g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type); + + return fn_type; +} + +static ZigTypeId container_to_type(ContainerKind kind) { + switch (kind) { + case ContainerKindStruct: + return ZigTypeIdStruct; + case ContainerKindEnum: + return ZigTypeIdEnum; + case ContainerKindUnion: + return ZigTypeIdUnion; + } + zig_unreachable(); +} + +// This is like get_partial_container_type except it's for the implicit root struct of files. +static ZigType *get_root_container_type(CodeGen *g, const char *full_name, Buf *bare_name, + RootStruct *root_struct) +{ + ZigType *entry = new_type_table_entry(ZigTypeIdStruct); + entry->data.structure.decls_scope = create_decls_scope(g, nullptr, nullptr, entry, entry, bare_name); + entry->data.structure.root_struct = root_struct; + entry->data.structure.layout = ContainerLayoutAuto; + + if (full_name[0] == '\0') { + buf_init_from_str(&entry->name, "(root)"); + } else { + buf_init_from_str(&entry->name, full_name); + } + + return entry; +} + +ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, + AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout) +{ + ZigTypeId type_id = container_to_type(kind); + ZigType *entry = new_container_type_entry(g, type_id, decl_node, scope, bare_name); + + 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; + } + + buf_init_from_str(&entry->name, full_name); + + return entry; +} + +ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, + Buf *type_name, UndefAllowed undef) +{ + Error err; + + ZigValue *result = g->pass1_arena->create(); + ZigValue *result_ptr = g->pass1_arena->create(); + result->special = ConstValSpecialUndef; + result->type = (type_entry == nullptr) ? g->builtin_types.entry_anytype : type_entry; + result_ptr->special = ConstValSpecialStatic; + result_ptr->type = get_pointer_to_type(g, result->type, false); + result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar; + result_ptr->data.x_ptr.special = ConstPtrSpecialRef; + result_ptr->data.x_ptr.data.ref.pointee = result; + + size_t backward_branch_count = 0; + size_t backward_branch_quota = default_backward_branch_quota; + if ((err = ir_eval_const_value(g, scope, node, result_ptr, + &backward_branch_count, &backward_branch_quota, + nullptr, nullptr, node, type_name, nullptr, nullptr, undef))) + { + return g->invalid_inst_gen->value; + } + return result; +} + +Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent_type, + ZigValue *parent_type_val, bool *is_zero_bits) +{ + Error err; + if (type_val->special != ConstValSpecialLazy) { + assert(type_val->special == ConstValSpecialStatic); + + // Self-referencing types via pointers are allowed and have non-zero size + ZigType *ty = type_val->data.x_type; + while (ty->id == ZigTypeIdPointer && + !ty->data.pointer.resolve_loop_flag_zero_bits) + { + ty = ty->data.pointer.child_type; + } + + if ((ty->id == ZigTypeIdStruct && ty->data.structure.resolve_loop_flag_zero_bits) || + (ty->id == ZigTypeIdUnion && ty->data.unionation.resolve_loop_flag_zero_bits) || + (ty->id == ZigTypeIdPointer && ty->data.pointer.resolve_loop_flag_zero_bits)) + { + *is_zero_bits = false; + return ErrorNone; + } + + if ((err = type_resolve(g, type_val->data.x_type, ResolveStatusZeroBitsKnown))) + return err; + + *is_zero_bits = (type_val->data.x_type->abi_size == 0); + return ErrorNone; + } + switch (type_val->data.x_lazy->id) { + case LazyValueIdInvalid: + case LazyValueIdAlignOf: + case LazyValueIdSizeOf: + case LazyValueIdTypeInfoDecls: + zig_unreachable(); + case LazyValueIdPtrType: { + LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy); + + if (parent_type_val == lazy_ptr_type->elem_type->value) { + // Does a struct which contains a pointer field to itself have bits? Yes. + *is_zero_bits = false; + return ErrorNone; + } else { + if (parent_type_val == nullptr) { + parent_type_val = type_val; + } + return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type, + parent_type_val, is_zero_bits); + } + } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + + // The sentinel counts as an extra element + if (lazy_array_type->length == 0 && lazy_array_type->sentinel == nullptr) { + *is_zero_bits = true; + return ErrorNone; + } + + if ((err = type_val_resolve_zero_bits(g, lazy_array_type->elem_type->value, + parent_type, nullptr, is_zero_bits))) + return err; + + return ErrorNone; + } + case LazyValueIdOptType: + case LazyValueIdSliceType: + case LazyValueIdErrUnionType: + *is_zero_bits = false; + return ErrorNone; + case LazyValueIdFnType: { + LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy); + *is_zero_bits = lazy_fn_type->is_generic; + return ErrorNone; + } + } + zig_unreachable(); +} + +Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) { + if (type_val->special != ConstValSpecialLazy) { + assert(type_val->special == ConstValSpecialStatic); + if (type_val->data.x_type == g->builtin_types.entry_anytype) { + *is_opaque_type = false; + return ErrorNone; + } + *is_opaque_type = (type_val->data.x_type->id == ZigTypeIdOpaque); + return ErrorNone; + } + switch (type_val->data.x_lazy->id) { + case LazyValueIdInvalid: + case LazyValueIdAlignOf: + case LazyValueIdSizeOf: + case LazyValueIdTypeInfoDecls: + zig_unreachable(); + case LazyValueIdSliceType: + case LazyValueIdPtrType: + case LazyValueIdFnType: + case LazyValueIdOptType: + case LazyValueIdErrUnionType: + case LazyValueIdArrayType: + *is_opaque_type = false; + return ErrorNone; + } + zig_unreachable(); +} + +static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type_val) { + if (type_val->special != ConstValSpecialLazy) { + return type_requires_comptime(g, type_val->data.x_type); + } + switch (type_val->data.x_lazy->id) { + case LazyValueIdInvalid: + case LazyValueIdAlignOf: + case LazyValueIdSizeOf: + case LazyValueIdTypeInfoDecls: + zig_unreachable(); + case LazyValueIdSliceType: { + LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_requires_comptime(g, lazy_slice_type->elem_type->value); + } + case LazyValueIdPtrType: { + LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value); + } + case LazyValueIdOptType: { + LazyValueOptType *lazy_opt_type = reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value); + } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_requires_comptime(g, lazy_array_type->elem_type->value); + } + case LazyValueIdFnType: { + LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy); + if (lazy_fn_type->is_generic) + return ReqCompTimeYes; + switch (type_val_resolve_requires_comptime(g, lazy_fn_type->return_type->value)) { + case ReqCompTimeInvalid: + return ReqCompTimeInvalid; + case ReqCompTimeYes: + return ReqCompTimeYes; + case ReqCompTimeNo: + break; + } + size_t param_count = lazy_fn_type->proto_node->data.fn_proto.params.length; + for (size_t i = 0; i < param_count; i += 1) { + AstNode *param_node = lazy_fn_type->proto_node->data.fn_proto.params.at(i); + bool param_is_var_args = param_node->data.param_decl.is_var_args; + if (param_is_var_args) break; + switch (type_val_resolve_requires_comptime(g, lazy_fn_type->param_types[i]->value)) { + case ReqCompTimeInvalid: + return ReqCompTimeInvalid; + case ReqCompTimeYes: + return ReqCompTimeYes; + case ReqCompTimeNo: + break; + } + } + return ReqCompTimeNo; + } + case LazyValueIdErrUnionType: { + LazyValueErrUnionType *lazy_err_union_type = + reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_requires_comptime(g, lazy_err_union_type->payload_type->value); + } + } + zig_unreachable(); +} + +Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val, + size_t *abi_size, size_t *size_in_bits) +{ + Error err; + +start_over: + if (type_val->special != ConstValSpecialLazy) { + assert(type_val->special == ConstValSpecialStatic); + ZigType *ty = type_val->data.x_type; + if ((err = type_resolve(g, ty, ResolveStatusSizeKnown))) + return err; + *abi_size = ty->abi_size; + *size_in_bits = ty->size_in_bits; + return ErrorNone; + } + switch (type_val->data.x_lazy->id) { + case LazyValueIdInvalid: + case LazyValueIdAlignOf: + case LazyValueIdSizeOf: + case LazyValueIdTypeInfoDecls: + zig_unreachable(); + case LazyValueIdSliceType: { + LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy); + bool is_zero_bits; + if ((err = type_val_resolve_zero_bits(g, lazy_slice_type->elem_type->value, nullptr, + nullptr, &is_zero_bits))) + { + return err; + } + if (is_zero_bits) { + *abi_size = g->builtin_types.entry_usize->abi_size; + *size_in_bits = g->builtin_types.entry_usize->size_in_bits; + } else { + *abi_size = g->builtin_types.entry_usize->abi_size * 2; + *size_in_bits = g->builtin_types.entry_usize->size_in_bits * 2; + } + return ErrorNone; + } + case LazyValueIdPtrType: { + LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy); + bool is_zero_bits; + if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr, + nullptr, &is_zero_bits))) + { + return err; + } + if (is_zero_bits) { + *abi_size = 0; + *size_in_bits = 0; + } else { + *abi_size = g->builtin_types.entry_usize->abi_size; + *size_in_bits = g->builtin_types.entry_usize->size_in_bits; + } + return ErrorNone; + } + case LazyValueIdFnType: + *abi_size = g->builtin_types.entry_usize->abi_size; + *size_in_bits = g->builtin_types.entry_usize->size_in_bits; + return ErrorNone; + case LazyValueIdOptType: + case LazyValueIdErrUnionType: + case LazyValueIdArrayType: + if ((err = ir_resolve_lazy(g, source_node, type_val))) + return err; + goto start_over; + } + zig_unreachable(); +} + +Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *type_val, uint32_t *abi_align) { + Error err; + if (type_val->special != ConstValSpecialLazy) { + assert(type_val->special == ConstValSpecialStatic); + ZigType *ty = type_val->data.x_type; + if (ty->id == ZigTypeIdPointer) { + *abi_align = g->builtin_types.entry_usize->abi_align; + return ErrorNone; + } + if ((err = type_resolve(g, ty, ResolveStatusAlignmentKnown))) + return err; + *abi_align = ty->abi_align; + return ErrorNone; + } + switch (type_val->data.x_lazy->id) { + case LazyValueIdInvalid: + case LazyValueIdAlignOf: + case LazyValueIdSizeOf: + case LazyValueIdTypeInfoDecls: + zig_unreachable(); + case LazyValueIdSliceType: + case LazyValueIdPtrType: + case LazyValueIdFnType: + *abi_align = g->builtin_types.entry_usize->abi_align; + return ErrorNone; + case LazyValueIdOptType: { + if ((err = ir_resolve_lazy(g, nullptr, type_val))) + return err; + + return type_val_resolve_abi_align(g, source_node, type_val, abi_align); + } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_abi_align(g, source_node, lazy_array_type->elem_type->value, abi_align); + } + case LazyValueIdErrUnionType: { + LazyValueErrUnionType *lazy_err_union_type = + reinterpret_cast(type_val->data.x_lazy); + uint32_t payload_abi_align; + if ((err = type_val_resolve_abi_align(g, source_node, lazy_err_union_type->payload_type->value, + &payload_abi_align))) + { + return err; + } + *abi_align = (payload_abi_align > g->err_tag_type->abi_align) ? + payload_abi_align : g->err_tag_type->abi_align; + return ErrorNone; + } + } + zig_unreachable(); +} + +static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigValue *type_val) { + if (type_val->special != ConstValSpecialLazy) { + return type_has_one_possible_value(g, type_val->data.x_type); + } + switch (type_val->data.x_lazy->id) { + case LazyValueIdInvalid: + case LazyValueIdAlignOf: + case LazyValueIdSizeOf: + case LazyValueIdTypeInfoDecls: + zig_unreachable(); + case LazyValueIdSliceType: // it has the len field + case LazyValueIdOptType: // it has the optional bit + case LazyValueIdFnType: + return OnePossibleValueNo; + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + if (lazy_array_type->length == 0) + return OnePossibleValueYes; + return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value); + } + case LazyValueIdPtrType: { + Error err; + bool zero_bits; + if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) { + return OnePossibleValueInvalid; + } + if (zero_bits) { + return OnePossibleValueYes; + } else { + return OnePossibleValueNo; + } + } + case LazyValueIdErrUnionType: { + LazyValueErrUnionType *lazy_err_union_type = + reinterpret_cast(type_val->data.x_lazy); + switch (type_val_resolve_has_one_possible_value(g, lazy_err_union_type->err_set_type->value)) { + case OnePossibleValueInvalid: + return OnePossibleValueInvalid; + case OnePossibleValueNo: + return OnePossibleValueNo; + case OnePossibleValueYes: + return type_val_resolve_has_one_possible_value(g, lazy_err_union_type->payload_type->value); + } + } + } + zig_unreachable(); +} + +ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { + Error err; + // Hot path for simple identifiers, to avoid unnecessary memory allocations. + if (node->type == NodeTypeSymbol) { + Buf *variable_name = node->data.symbol_expr.symbol; + if (buf_eql_str(variable_name, "_")) + goto abort_hot_path; + ZigType *primitive_type; + if ((err = get_primitive_type(g, variable_name, &primitive_type))) { + goto abort_hot_path; + } else { + return primitive_type; + } +abort_hot_path:; + } + ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, + nullptr, UndefBad); + if (type_is_invalid(result->type)) + return g->builtin_types.entry_invalid; + src_assert(result->special == ConstValSpecialStatic, node); + src_assert(result->data.x_type != nullptr, node); + return result->data.x_type; +} + +ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { + ZigType *fn_type = new_type_table_entry(ZigTypeIdFn); + buf_resize(&fn_type->name, 0); + buf_appendf(&fn_type->name, "fn("); + size_t i = 0; + for (; i < fn_type_id->next_param_index; i += 1) { + const char *comma_str = (i == 0) ? "" : ","; + buf_appendf(&fn_type->name, "%s%s", comma_str, + buf_ptr(&fn_type_id->param_info[i].type->name)); + } + for (; i < fn_type_id->param_count; i += 1) { + const char *comma_str = (i == 0) ? "" : ","; + buf_appendf(&fn_type->name, "%sanytype", comma_str); + } + buf_append_str(&fn_type->name, ")"); + if (fn_type_id->cc != CallingConventionUnspecified) { + buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc)); + } + buf_append_str(&fn_type->name, " anytype"); + + fn_type->data.fn.fn_type_id = *fn_type_id; + fn_type->data.fn.is_generic = true; + fn_type->abi_size = 0; + fn_type->size_in_bits = 0; + fn_type->abi_align = 0; + return fn_type; +} + +CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto) { + // Compatible with the C ABI + if (fn_proto->is_extern || fn_proto->is_export) + return CallingConventionC; + + return CallingConventionUnspecified; +} + +void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc) { + assert(proto_node->type == NodeTypeFnProto); + AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; + + fn_type_id->cc = cc; + fn_type_id->param_count = fn_proto->params.length; + fn_type_id->param_info = heap::c_allocator.allocate(param_count_alloc); + fn_type_id->next_param_index = 0; + fn_type_id->is_var_args = fn_proto->is_var_args; +} + +static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { + ZigValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), + nullptr, UndefBad); + if (type_is_invalid(align_result->type)) + return false; + + uint32_t align_bytes = bigint_as_u32(&align_result->data.x_bigint); + if (align_bytes == 0) { + add_node_error(g, node, buf_sprintf("alignment must be >= 1")); + return false; + } + if (!is_power_of_2(align_bytes)) { + add_node_error(g, node, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); + return false; + } + + *result = align_bytes; + return true; +} + +static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) { + ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, 0, 0, 0, false); + ZigType *str_type = get_slice_type(g, ptr_type); + ZigValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr, UndefBad); + if (type_is_invalid(result_val->type)) + return false; + + ZigValue *ptr_field = result_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *len_field = result_val->data.x_struct.fields[slice_len_index]; + + assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); + ZigValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; + if (array_val->data.x_array.special == ConstArraySpecialBuf) { + *out_buffer = array_val->data.x_array.data.s_buf; + return true; + } + expand_undef_array(g, array_val); + size_t len = bigint_as_usize(&len_field->data.x_bigint); + Buf *result = buf_alloc(); + buf_resize(result, len); + for (size_t i = 0; i < len; i += 1) { + size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i; + ZigValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index]; + if (char_val->special == ConstValSpecialUndef) { + add_node_error(g, node, buf_sprintf("use of undefined value")); + return false; + } + uint64_t big_c = bigint_as_u64(&char_val->data.x_bigint); + assert(big_c <= UINT8_MAX); + uint8_t c = (uint8_t)big_c; + buf_ptr(result)[i] = c; + } + *out_buffer = result; + return true; +} + +static Error emit_error_unless_type_allowed_in_packed_container(CodeGen *g, ZigType *type_entry, + AstNode *source_node, const char* container_name) +{ + Error err; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + add_node_error(g, source_node, + buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation", + buf_ptr(&type_entry->name), container_name)); + return ErrorSemanticAnalyzeFail; + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdFn: + case ZigTypeIdVector: + return ErrorNone; + case ZigTypeIdArray: { + ZigType *elem_type = type_entry->data.array.child_type; + if ((err = emit_error_unless_type_allowed_in_packed_container(g, elem_type, source_node, container_name))) + return err; + // TODO revisit this when doing https://github.com/ziglang/zig/issues/1512 + if (type_size(g, type_entry) * 8 == type_size_bits(g, type_entry)) + return ErrorNone; + add_node_error(g, source_node, + buf_sprintf("array of '%s' not allowed in packed %s due to padding bits", + buf_ptr(&elem_type->name), container_name)); + return ErrorSemanticAnalyzeFail; + } + case ZigTypeIdStruct: + switch (type_entry->data.structure.layout) { + case ContainerLayoutPacked: + case ContainerLayoutExtern: + return ErrorNone; + case ContainerLayoutAuto: + add_node_error(g, source_node, + buf_sprintf("non-packed, non-extern struct '%s' not allowed in packed %s; no guaranteed in-memory representation", + buf_ptr(&type_entry->name), container_name)); + return ErrorSemanticAnalyzeFail; + } + zig_unreachable(); + case ZigTypeIdUnion: + switch (type_entry->data.unionation.layout) { + case ContainerLayoutPacked: + case ContainerLayoutExtern: + return ErrorNone; + case ContainerLayoutAuto: + add_node_error(g, source_node, + buf_sprintf("non-packed, non-extern union '%s' not allowed in packed %s; no guaranteed in-memory representation", + buf_ptr(&type_entry->name), container_name)); + return ErrorSemanticAnalyzeFail; + } + zig_unreachable(); + case ZigTypeIdOptional: { + ZigType *ptr_type; + if ((err = get_codegen_ptr_type(g, type_entry, &ptr_type))) return err; + if (ptr_type != nullptr) return ErrorNone; + + add_node_error(g, source_node, + buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation", + buf_ptr(&type_entry->name), container_name)); + return ErrorSemanticAnalyzeFail; + } + case ZigTypeIdEnum: { + AstNode *decl_node = type_entry->data.enumeration.decl_node; + if (decl_node->data.container_decl.init_arg_expr != nullptr) { + return ErrorNone; + } + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation", + buf_ptr(&type_entry->name), container_name)); + add_error_note(g, msg, decl_node, + buf_sprintf("enum declaration does not specify an integer tag type")); + return ErrorSemanticAnalyzeFail; + } + } + zig_unreachable(); +} + +static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry, + AstNode *source_node) +{ + return emit_error_unless_type_allowed_in_packed_container(g, type_entry, source_node, "struct"); +} + +static Error emit_error_unless_type_allowed_in_packed_union(CodeGen *g, ZigType *type_entry, + AstNode *source_node) +{ + return emit_error_unless_type_allowed_in_packed_container(g, type_entry, source_node, "union"); +} + +Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) { + Error err; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdBoundFn: + case ZigTypeIdVoid: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + *result = false; + return ErrorNone; + case ZigTypeIdOpaque: + case ZigTypeIdUnreachable: + case ZigTypeIdBool: + *result = true; + return ErrorNone; + case ZigTypeIdInt: + switch (type_entry->data.integral.bit_count) { + case 8: + case 16: + case 32: + case 64: + case 128: + *result = true; + return ErrorNone; + default: + *result = false; + return ErrorNone; + } + case ZigTypeIdVector: + return type_allowed_in_extern(g, type_entry->data.vector.elem_type, result); + case ZigTypeIdFloat: + *result = true; + return ErrorNone; + case ZigTypeIdArray: + return type_allowed_in_extern(g, type_entry->data.array.child_type, result); + case ZigTypeIdFn: + *result = !calling_convention_allows_zig_types(type_entry->data.fn.fn_type_id.cc); + return ErrorNone; + case ZigTypeIdPointer: + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return err; + if (!type_has_bits(g, type_entry)) { + *result = false; + return ErrorNone; + } + *result = true; + return ErrorNone; + case ZigTypeIdStruct: + *result = type_entry->data.structure.layout == ContainerLayoutExtern || + type_entry->data.structure.layout == ContainerLayoutPacked; + return ErrorNone; + case ZigTypeIdOptional: { + ZigType *child_type = type_entry->data.maybe.child_type; + if (child_type->id != ZigTypeIdPointer && child_type->id != ZigTypeIdFn) { + *result = false; + return ErrorNone; + } + if (!type_is_nonnull_ptr(g, child_type)) { + *result = false; + return ErrorNone; + } + return type_allowed_in_extern(g, child_type, result); + } + case ZigTypeIdEnum: + *result = type_entry->data.enumeration.layout == ContainerLayoutExtern || + type_entry->data.enumeration.layout == ContainerLayoutPacked; + return ErrorNone; + case ZigTypeIdUnion: + *result = type_entry->data.unionation.layout == ContainerLayoutExtern || + type_entry->data.unionation.layout == ContainerLayoutPacked; + return ErrorNone; + } + zig_unreachable(); +} + +ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) { + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "@typeInfo(@typeInfo(@TypeOf(%s)).Fn.return_type.?).ErrorUnion.error_set", buf_ptr(&fn_entry->symbol_name)); + err_set_type->data.error_set.err_count = 0; + err_set_type->data.error_set.errors = nullptr; + err_set_type->data.error_set.infer_fn = fn_entry; + err_set_type->data.error_set.incomplete = true; + err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits; + err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align; + err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size; + + return err_set_type; +} + +static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, ZigFn *fn_entry, + CallingConvention cc) +{ + assert(proto_node->type == NodeTypeFnProto); + AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; + Error err; + + FnTypeId fn_type_id = {0}; + init_fn_type_id(&fn_type_id, proto_node, cc, proto_node->data.fn_proto.params.length); + + for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { + AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); + assert(param_node->type == NodeTypeParamDecl); + + bool param_is_comptime = param_node->data.param_decl.is_comptime; + bool param_is_var_args = param_node->data.param_decl.is_var_args; + + if (param_is_comptime) { + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + add_node_error(g, param_node, + buf_sprintf("comptime parameter not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + if (param_node->data.param_decl.type != nullptr) { + ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); + if (type_is_invalid(type_entry)) { + return g->builtin_types.entry_invalid; + } + 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.next_param_index += 1; + } + + return get_generic_fn_type(g, &fn_type_id); + } else if (param_is_var_args) { + if (fn_type_id.cc == CallingConventionC) { + fn_type_id.param_count = fn_type_id.next_param_index; + continue; + } else { + add_node_error(g, param_node, + buf_sprintf("var args only allowed in functions with C calling convention")); + return g->builtin_types.entry_invalid; + } + } else if (param_node->data.param_decl.anytype_token != nullptr) { + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + add_node_error(g, param_node, + buf_sprintf("parameter of type 'anytype' not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + return get_generic_fn_type(g, &fn_type_id); + } + + ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); + if (type_is_invalid(type_entry)) { + return g->builtin_types.entry_invalid; + } + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return g->builtin_types.entry_invalid; + if (!type_has_bits(g, type_entry)) { + add_node_error(g, param_node->data.param_decl.type, + buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", + buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + } + + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + bool ok_type; + if ((err = type_allowed_in_extern(g, type_entry, &ok_type))) + return g->builtin_types.entry_invalid; + if (!ok_type) { + add_node_error(g, param_node->data.param_decl.type, + buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'", + buf_ptr(&type_entry->name), + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + } + + if(!is_valid_param_type(type_entry)){ + if(type_entry->id == ZigTypeIdOpaque){ + add_node_error(g, param_node->data.param_decl.type, + buf_sprintf("parameter of opaque type '%s' not allowed", buf_ptr(&type_entry->name))); + } else { + 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; + } + + switch (type_requires_comptime(g, type_entry)) { + case ReqCompTimeNo: + break; + case ReqCompTimeYes: + add_node_error(g, param_node->data.param_decl.type, + buf_sprintf("parameter of type '%s' must be declared comptime", + buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + case ReqCompTimeInvalid: + return g->builtin_types.entry_invalid; + } + + FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; + param_info->type = type_entry; + param_info->is_noalias = param_node->data.param_decl.is_noalias; + } + + if (fn_proto->align_expr != nullptr) { + if (target_is_wasm(g->zig_target)) { + // In Wasm, specifying alignment of function pointers makes little sense + // since function pointers are in fact indices to a Wasm table, therefore + // any alignment check on those is invalid. This can cause unexpected + // behaviour when checking expected alignment with `@ptrToInt(fn_ptr)` + // or similar. This commit proposes to make `align` expressions a + // compile error when compiled to Wasm architecture. + // + // Some references: + // [1] [Mozilla: WebAssembly Tables](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format#WebAssembly_tables) + // [2] [Sunfishcode's Wasm Ref Manual](https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#indirect-call) + add_node_error(g, fn_proto->align_expr, + buf_sprintf("align(N) expr is not allowed on function prototypes in wasm32/wasm64")); + return g->builtin_types.entry_invalid; + } + if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &fn_type_id.alignment)) { + return g->builtin_types.entry_invalid; + } + fn_entry->align_bytes = fn_type_id.alignment; + } + + if (fn_proto->return_anytype_token != nullptr) { + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + add_node_error(g, fn_proto->return_type, + buf_sprintf("return type 'anytype' not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + add_node_error(g, proto_node, + buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); + return g->builtin_types.entry_invalid; + } + + ZigType *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type); + if (type_is_invalid(specified_return_type)) { + fn_type_id.return_type = g->builtin_types.entry_invalid; + return g->builtin_types.entry_invalid; + } + + if(!is_valid_return_type(specified_return_type)){ + ErrorMsg* msg = add_node_error(g, fn_proto->return_type, + buf_sprintf("%s return type '%s' not allowed", type_id_name(specified_return_type->id), buf_ptr(&specified_return_type->name))); + Tld *tld = find_decl(g, &fn_entry->fndef_scope->base, &specified_return_type->name); + if (tld != nullptr) { + add_error_note(g, msg, tld->source_node, buf_sprintf("type declared here")); + } + return g->builtin_types.entry_invalid; + } + + if (fn_proto->auto_err_set) { + ZigType *inferred_err_set_type = get_auto_err_set_type(g, fn_entry); + if ((err = type_resolve(g, specified_return_type, ResolveStatusSizeKnown))) + return g->builtin_types.entry_invalid; + fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type); + } else { + fn_type_id.return_type = specified_return_type; + } + + if (!calling_convention_allows_zig_types(fn_type_id.cc) && + fn_type_id.return_type->id != ZigTypeIdVoid) + { + if ((err = type_resolve(g, fn_type_id.return_type, ResolveStatusSizeKnown))) + return g->builtin_types.entry_invalid; + bool ok_type; + if ((err = type_allowed_in_extern(g, fn_type_id.return_type, &ok_type))) + return g->builtin_types.entry_invalid; + if (!ok_type) { + add_node_error(g, fn_proto->return_type, + buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", + buf_ptr(&fn_type_id.return_type->name), + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + } + + switch (type_requires_comptime(g, fn_type_id.return_type)) { + case ReqCompTimeInvalid: + return g->builtin_types.entry_invalid; + case ReqCompTimeYes: + return get_generic_fn_type(g, &fn_type_id); + case ReqCompTimeNo: + break; + } + + return get_fn_type(g, &fn_type_id); +} + +bool is_valid_return_type(ZigType* type) { + switch (type->id) { + case ZigTypeIdInvalid: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOpaque: + return false; + default: + return true; + } + zig_unreachable(); +} + +bool is_valid_param_type(ZigType* type) { + switch (type->id) { + case ZigTypeIdInvalid: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOpaque: + case ZigTypeIdUnreachable: + return false; + default: + return true; + } + zig_unreachable(); +} + +bool type_is_invalid(ZigType *type_entry) { + switch (type_entry->id) { + case ZigTypeIdInvalid: + return true; + case ZigTypeIdStruct: + return type_entry->data.structure.resolve_status == ResolveStatusInvalid; + case ZigTypeIdUnion: + return type_entry->data.unionation.resolve_status == ResolveStatusInvalid; + case ZigTypeIdEnum: + return type_entry->data.enumeration.resolve_status == ResolveStatusInvalid; + case ZigTypeIdFnFrame: + return type_entry->data.frame.reported_loop_err; + default: + return false; + } + zig_unreachable(); +} + +struct SrcField { + const char *name; + ZigType *ty; + unsigned align; +}; + +static ZigType *get_struct_type(CodeGen *g, const char *type_name, SrcField fields[], size_t field_count, + unsigned min_abi_align) +{ + ZigType *struct_type = new_type_table_entry(ZigTypeIdStruct); + + buf_init_from_str(&struct_type->name, type_name); + + struct_type->data.structure.src_field_count = field_count; + struct_type->data.structure.gen_field_count = 0; + struct_type->data.structure.resolve_status = ResolveStatusSizeKnown; + struct_type->data.structure.fields = alloc_type_struct_fields(field_count); + struct_type->data.structure.fields_by_name.init(field_count); + + size_t abi_align = min_abi_align; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + field->name = buf_create_from_str(fields[i].name); + field->type_entry = fields[i].ty; + field->src_index = i; + field->align = fields[i].align; + + if (type_has_bits(g, field->type_entry)) { + assert(type_is_resolved(field->type_entry, ResolveStatusSizeKnown)); + unsigned field_abi_align = max(field->align, field->type_entry->abi_align); + if (field_abi_align > abi_align) { + abi_align = field_abi_align; + } + } + + auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field); + assert(prev_entry == nullptr); + } + + size_t next_offset = 0; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + if (!type_has_bits(g, field->type_entry)) + continue; + + field->offset = next_offset; + + // find the next non-zero-byte field for offset calculations + size_t next_src_field_index = i + 1; + for (; next_src_field_index < field_count; next_src_field_index += 1) { + if (type_has_bits(g, struct_type->data.structure.fields[next_src_field_index]->type_entry)) + break; + } + size_t next_abi_align; + if (next_src_field_index == field_count) { + next_abi_align = abi_align; + } else { + next_abi_align = max(fields[next_src_field_index].align, + struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align); + } + next_offset = next_field_offset(next_offset, abi_align, field->type_entry->abi_size, next_abi_align); + } + + struct_type->abi_align = abi_align; + struct_type->abi_size = next_offset; + struct_type->size_in_bits = next_offset * 8; + + return struct_type; +} + +static size_t get_store_size_bytes(size_t size_in_bits) { + return (size_in_bits + 7) / 8; +} + +static size_t get_abi_align_bytes(size_t size_in_bits, size_t pointer_size_bytes) { + size_t store_size_bytes = get_store_size_bytes(size_in_bits); + if (store_size_bytes >= pointer_size_bytes) + return pointer_size_bytes; + return round_to_next_power_of_2(store_size_bytes); +} + +static size_t get_abi_size_bytes(size_t size_in_bits, size_t pointer_size_bytes) { + size_t store_size_bytes = get_store_size_bytes(size_in_bits); + size_t abi_align = get_abi_align_bytes(size_in_bits, pointer_size_bytes); + return align_forward(store_size_bytes, abi_align); +} + +ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field) { + Error err; + if (struct_field->type_entry == nullptr) { + if ((err = ir_resolve_lazy(g, struct_field->decl_node, struct_field->type_val))) { + return nullptr; + } + struct_field->type_entry = struct_field->type_val->data.x_type; + } + return struct_field->type_entry; +} + +static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { + assert(struct_type->id == ZigTypeIdStruct); + + Error err; + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown) + return ErrorNone; + + if ((err = resolve_struct_alignment(g, struct_type))) + return err; + + AstNode *decl_node = struct_type->data.structure.decl_node; + + if (struct_type->data.structure.resolve_loop_flag_other) { + if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("struct '%s' depends on itself", buf_ptr(&struct_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0); + + size_t field_count = struct_type->data.structure.src_field_count; + + bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked); + struct_type->data.structure.resolve_loop_flag_other = true; + + uint32_t *host_int_bytes = packed ? heap::c_allocator.allocate(struct_type->data.structure.gen_field_count) : nullptr; + + size_t packed_bits_offset = 0; + size_t next_offset = 0; + size_t first_packed_bits_offset_misalign = SIZE_MAX; + size_t gen_field_index = 0; + size_t size_in_bits = 0; + size_t abi_align = struct_type->abi_align; + + // Calculate offsets + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + if (field->gen_index == SIZE_MAX) + continue; + + field->gen_index = gen_field_index; + field->offset = next_offset; + + if (packed) { + ZigType *field_type = resolve_struct_field_type(g, field); + if (field_type == nullptr) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return err; + } + if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field->type_entry, field->decl_node))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return err; + } + + 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; + + size_in_bits += 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 + field->bit_offset_in_host = packed_bits_offset - first_packed_bits_offset_misalign; + + size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign; + size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes); + if (full_abi_size * 8 == full_bit_count) { + // next field recovers ABI alignment + host_int_bytes[gen_field_index] = full_abi_size; + gen_field_index += 1; + // TODO: https://github.com/ziglang/zig/issues/1512 + next_offset = next_field_offset(next_offset, abi_align, full_abi_size, 1); + size_in_bits = next_offset * 8; + + first_packed_bits_offset_misalign = SIZE_MAX; + } + } else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) { + first_packed_bits_offset_misalign = packed_bits_offset; + field->bit_offset_in_host = 0; + } else { + // This is a byte-aligned field (both start and end) in a packed struct. + host_int_bytes[gen_field_index] = field_type->size_in_bits / 8; + field->bit_offset_in_host = 0; + gen_field_index += 1; + // TODO: https://github.com/ziglang/zig/issues/1512 + next_offset = next_field_offset(next_offset, abi_align, field_type->size_in_bits / 8, 1); + size_in_bits = next_offset * 8; + } + packed_bits_offset = next_packed_bits_offset; + } else { + size_t field_abi_size; + size_t field_size_in_bits; + if ((err = type_val_resolve_abi_size(g, field->decl_node, field->type_val, + &field_abi_size, &field_size_in_bits))) + { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return err; + } + + gen_field_index += 1; + size_t next_src_field_index = i + 1; + for (; next_src_field_index < field_count; next_src_field_index += 1) { + if (struct_type->data.structure.fields[next_src_field_index]->gen_index != SIZE_MAX) { + break; + } + } + size_t next_align = (next_src_field_index == field_count) ? + abi_align : struct_type->data.structure.fields[next_src_field_index]->align; + next_offset = next_field_offset(next_offset, abi_align, field_abi_size, next_align); + size_in_bits = next_offset * 8; + } + } + if (first_packed_bits_offset_misalign != SIZE_MAX) { + size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign; + size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes); + next_offset = next_field_offset(next_offset, abi_align, full_abi_size, abi_align); + host_int_bytes[gen_field_index] = full_abi_size; + gen_field_index += 1; + } + + struct_type->abi_size = next_offset; + struct_type->size_in_bits = size_in_bits; + struct_type->data.structure.resolve_status = ResolveStatusSizeKnown; + struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index; + struct_type->data.structure.resolve_loop_flag_other = false; + struct_type->data.structure.host_int_bytes = host_int_bytes; + + + // Resolve types for fields + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + ZigType *field_type = resolve_struct_field_type(g, field); + if (field_type == nullptr) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return err; + } + + if (struct_type->data.structure.layout == ContainerLayoutExtern) { + bool ok_type; + if ((err = type_allowed_in_extern(g, field_type, &ok_type))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (!ok_type) { + add_node_error(g, field->decl_node, + buf_sprintf("extern structs cannot contain fields of type '%s'", + buf_ptr(&field_type->name))); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } + } + + return ErrorNone; +} + +static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) { + assert(union_type->id == ZigTypeIdUnion); + + Error err; + + if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown) + return ErrorNone; + if ((err = resolve_union_zero_bits(g, union_type))) + return err; + if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown) + return ErrorNone; + + AstNode *decl_node = union_type->data.structure.decl_node; + + if (union_type->data.unionation.resolve_loop_flag_other) { + if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("union '%s' depends on itself", buf_ptr(&union_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + // set temporary flag + union_type->data.unionation.resolve_loop_flag_other = true; + + TypeUnionField *most_aligned_union_member = nullptr; + uint32_t field_count = union_type->data.unionation.src_field_count; + bool packed = union_type->data.unionation.layout == ContainerLayoutPacked; + + for (uint32_t i = 0; i < field_count; i += 1) { + TypeUnionField *field = &union_type->data.unionation.fields[i]; + if (field->gen_index == UINT32_MAX) + continue; + + AstNode *align_expr = nullptr; + if (union_type->data.unionation.decl_node->type == NodeTypeContainerDecl) { + align_expr = field->decl_node->data.struct_field.align_expr; + } + if (align_expr != nullptr) { + if (!analyze_const_align(g, &union_type->data.unionation.decls_scope->base, align_expr, + &field->align)) + { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + add_node_error(g, field->decl_node, + buf_create_from_str("TODO implement field alignment syntax for unions. https://github.com/ziglang/zig/issues/3125")); + } else if (packed) { + field->align = 1; + } else if (field->type_entry != nullptr) { + if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return err; + } + field->align = field->type_entry->abi_align; + } else { + if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) { + if (g->trace_err != nullptr) { + g->trace_err = add_error_note(g, g->trace_err, field->decl_node, + buf_create_from_str("while checking this field")); + } + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return err; + } + if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + } + + if (most_aligned_union_member == nullptr || field->align > most_aligned_union_member->align) { + most_aligned_union_member = field; + } + } + + // unset temporary flag + union_type->data.unionation.resolve_loop_flag_other = false; + union_type->data.unionation.resolve_status = ResolveStatusAlignmentKnown; + union_type->data.unionation.most_aligned_union_member = most_aligned_union_member; + + ZigType *tag_type = union_type->data.unionation.tag_type; + if (tag_type != nullptr && type_has_bits(g, tag_type)) { + if ((err = type_resolve(g, tag_type, ResolveStatusAlignmentKnown))) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (most_aligned_union_member == nullptr) { + union_type->abi_align = tag_type->abi_align; + union_type->data.unionation.gen_tag_index = SIZE_MAX; + union_type->data.unionation.gen_union_index = SIZE_MAX; + } else if (tag_type->abi_align > most_aligned_union_member->align) { + union_type->abi_align = tag_type->abi_align; + union_type->data.unionation.gen_tag_index = 0; + union_type->data.unionation.gen_union_index = 1; + } else { + union_type->abi_align = most_aligned_union_member->align; + union_type->data.unionation.gen_union_index = 0; + union_type->data.unionation.gen_tag_index = 1; + } + } else { + assert(most_aligned_union_member != nullptr); + union_type->abi_align = most_aligned_union_member->align; + union_type->data.unionation.gen_union_index = SIZE_MAX; + union_type->data.unionation.gen_tag_index = SIZE_MAX; + } + + return ErrorNone; +} + +ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field) { + Error err; + if (union_field->type_entry == nullptr) { + if ((err = ir_resolve_lazy(g, union_field->decl_node, union_field->type_val))) { + return nullptr; + } + union_field->type_entry = union_field->type_val->data.x_type; + } + return union_field->type_entry; +} + +static Error resolve_union_type(CodeGen *g, ZigType *union_type) { + assert(union_type->id == ZigTypeIdUnion); + + Error err; + + if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (union_type->data.unionation.resolve_status >= ResolveStatusSizeKnown) + return ErrorNone; + + if ((err = resolve_union_alignment(g, union_type))) + return err; + + AstNode *decl_node = union_type->data.unionation.decl_node; + + uint32_t field_count = union_type->data.unionation.src_field_count; + TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member; + + assert(union_type->data.unionation.fields); + + size_t union_abi_size = 0; + size_t union_size_in_bits = 0; + + if (union_type->data.unionation.resolve_loop_flag_other) { + if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("union '%s' depends on itself", buf_ptr(&union_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + // set temporary flag + union_type->data.unionation.resolve_loop_flag_other = true; + + const bool is_packed = union_type->data.unionation.layout == ContainerLayoutPacked; + + for (uint32_t i = 0; i < field_count; i += 1) { + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + ZigType *field_type = resolve_union_field_type(g, union_field); + if (field_type == nullptr) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + if (is_packed) { + if ((err = emit_error_unless_type_allowed_in_packed_union(g, field_type, union_field->decl_node))) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return err; + } + } + + if (type_is_invalid(union_type)) + return ErrorSemanticAnalyzeFail; + + if (!type_has_bits(g, field_type)) + continue; + + union_abi_size = max(union_abi_size, field_type->abi_size); + union_size_in_bits = max(union_size_in_bits, field_type->size_in_bits); + } + + // The union itself for now has to be treated as being independently aligned. + // See https://github.com/ziglang/zig/issues/2166. + if (most_aligned_union_member != nullptr) { + union_abi_size = align_forward(union_abi_size, most_aligned_union_member->align); + } + + // unset temporary flag + union_type->data.unionation.resolve_loop_flag_other = false; + union_type->data.unionation.resolve_status = ResolveStatusSizeKnown; + union_type->data.unionation.union_abi_size = union_abi_size; + + ZigType *tag_type = union_type->data.unionation.tag_type; + if (tag_type != nullptr && type_has_bits(g, tag_type)) { + if ((err = type_resolve(g, tag_type, ResolveStatusSizeKnown))) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (most_aligned_union_member == nullptr) { + union_type->abi_size = tag_type->abi_size; + union_type->size_in_bits = tag_type->size_in_bits; + } else { + size_t field_sizes[2]; + size_t field_aligns[2]; + field_sizes[union_type->data.unionation.gen_tag_index] = tag_type->abi_size; + field_aligns[union_type->data.unionation.gen_tag_index] = tag_type->abi_align; + field_sizes[union_type->data.unionation.gen_union_index] = union_abi_size; + field_aligns[union_type->data.unionation.gen_union_index] = most_aligned_union_member->align; + size_t field2_offset = next_field_offset(0, union_type->abi_align, field_sizes[0], field_aligns[1]); + union_type->abi_size = next_field_offset(field2_offset, union_type->abi_align, field_sizes[1], union_type->abi_align); + union_type->size_in_bits = union_type->abi_size * 8; + } + } else { + union_type->abi_size = union_abi_size; + union_type->size_in_bits = union_size_in_bits; + } + + return ErrorNone; +} + +static Error type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty, bool *result) { + // Only integer types are allowed by the C ABI + if(ty->id != ZigTypeIdInt) { + *result = false; + return ErrorNone; + } + + // According to the ANSI C standard the enumeration type should be either a + // signed char, a signed integer or an unsigned one. But GCC/Clang allow + // other integral types as a compiler extension so let's accomodate them + // aswell. + return type_allowed_in_extern(g, ty, result); +} + +static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { + Error err; + assert(enum_type->id == ZigTypeIdEnum); + + if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (enum_type->data.enumeration.resolve_status >= ResolveStatusZeroBitsKnown) + return ErrorNone; + + AstNode *decl_node = enum_type->data.enumeration.decl_node; + + if (enum_type->data.enumeration.resolve_loop_flag) { + if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("enum '%s' depends on itself", + buf_ptr(&enum_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + enum_type->data.enumeration.resolve_loop_flag = true; + + uint32_t field_count; + if (decl_node->type == NodeTypeContainerDecl) { + assert(!enum_type->data.enumeration.fields); + field_count = (uint32_t)decl_node->data.container_decl.fields.length; + } else { + field_count = enum_type->data.enumeration.src_field_count + enum_type->data.enumeration.non_exhaustive; + } + + if (field_count == 0) { + add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); + enum_type->data.enumeration.src_field_count = field_count; + enum_type->data.enumeration.fields = nullptr; + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + Scope *scope = &enum_type->data.enumeration.decls_scope->base; + + ZigType *tag_int_type; + if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { + tag_int_type = get_c_int_type(g, CIntTypeInt); + } else { + tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + } + + enum_type->size_in_bits = tag_int_type->size_in_bits; + enum_type->abi_size = tag_int_type->abi_size; + enum_type->abi_align = tag_int_type->abi_align; + + ZigType *wanted_tag_int_type = nullptr; + if (decl_node->type == NodeTypeContainerDecl) { + if (decl_node->data.container_decl.init_arg_expr != nullptr) { + wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); + } + } else { + wanted_tag_int_type = enum_type->data.enumeration.tag_int_type; + } + + if (wanted_tag_int_type != nullptr) { + if (type_is_invalid(wanted_tag_int_type)) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } else if (wanted_tag_int_type->id != ZigTypeIdInt && + wanted_tag_int_type->id != ZigTypeIdComptimeInt) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); + } else { + if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { + bool ok_type; + if ((err = type_is_valid_extern_enum_tag(g, wanted_tag_int_type, &ok_type))) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + return err; + } + if (!ok_type) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("'%s' is not a valid tag type for an extern enum", + buf_ptr(&wanted_tag_int_type->name))); + add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid")); + return ErrorSemanticAnalyzeFail; + } + } + tag_int_type = wanted_tag_int_type; + } + } + + enum_type->data.enumeration.tag_int_type = tag_int_type; + enum_type->size_in_bits = tag_int_type->size_in_bits; + enum_type->abi_size = tag_int_type->abi_size; + enum_type->abi_align = tag_int_type->abi_align; + + BigInt bi_one; + bigint_init_unsigned(&bi_one, 1); + + if (decl_node->type == NodeTypeContainerDecl) { + AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1); + if (buf_eql_str(last_field_node->data.struct_field.name, "_")) { + if (last_field_node->data.struct_field.value != nullptr) { + add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + if (decl_node->data.container_decl.init_arg_expr == nullptr) { + add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum must specify size")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + enum_type->data.enumeration.non_exhaustive = true; + } else { + enum_type->data.enumeration.non_exhaustive = false; + } + } + + if (enum_type->data.enumeration.non_exhaustive) { + field_count -= 1; + if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) { + add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum specifies every value")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + } + + if (decl_node->type == NodeTypeContainerDecl) { + enum_type->data.enumeration.src_field_count = field_count; + enum_type->data.enumeration.fields = heap::c_allocator.allocate(field_count); + enum_type->data.enumeration.fields_by_name.init(field_count); + + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + + TypeEnumField *last_enum_field = nullptr; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; + type_enum_field->name = field_node->data.struct_field.name; + type_enum_field->decl_index = field_i; + type_enum_field->decl_node = field_node; + + if (field_node->data.struct_field.type != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("structs and unions, not enums, support field types")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + } else if (field_node->data.struct_field.align_expr != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr, + buf_sprintf("structs and unions, not enums, support field alignment")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + } + + if (buf_eql_str(type_enum_field->name, "_")) { + add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + + auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); + if (field_entry != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); + add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + continue; + } + + AstNode *tag_value = field_node->data.struct_field.value; + + if (tag_value != nullptr) { + // A user-specified value is available + ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, + nullptr, UndefBad); + if (type_is_invalid(result->type)) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + continue; + } + + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt); + + bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); + } else { + // No value was explicitly specified: allocate the last value + 1 + // or, if this is the first element, zero + if (last_enum_field != nullptr) { + bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one); + } else { + bigint_init_unsigned(&type_enum_field->value, 0); + } + + // Make sure we can represent this number with tag_int_type + if (!bigint_fits_in_bits(&type_enum_field->value, + tag_int_type->size_in_bits, + tag_int_type->data.integral.is_signed)) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &type_enum_field->value, 10); + add_node_error(g, field_node, + buf_sprintf("enumeration value %s too large for type '%s'", + buf_ptr(val_buf), buf_ptr(&tag_int_type->name))); + + break; + } + } + + // Make sure the value is unique + auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); + if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &type_enum_field->value, 10); + + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); + add_error_note(g, msg, entry->value, + buf_sprintf("other occurrence here")); + } + + last_enum_field = type_enum_field; + } + occupied_tag_values.deinit(); + } + + if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + + enum_type->data.enumeration.resolve_loop_flag = false; + enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown; + + return ErrorNone; +} + +static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { + assert(struct_type->id == ZigTypeIdStruct); + + Error err; + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (struct_type->data.structure.resolve_status >= ResolveStatusZeroBitsKnown) + return ErrorNone; + + AstNode *decl_node = struct_type->data.structure.decl_node; + + if (struct_type->data.structure.resolve_loop_flag_zero_bits) { + if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("struct '%s' depends on itself", + buf_ptr(&struct_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + struct_type->data.structure.resolve_loop_flag_zero_bits = true; + + size_t field_count; + if (decl_node->type == NodeTypeContainerDecl) { + field_count = decl_node->data.container_decl.fields.length; + struct_type->data.structure.src_field_count = (uint32_t)field_count; + + src_assert(struct_type->data.structure.fields == nullptr, decl_node); + struct_type->data.structure.fields = alloc_type_struct_fields(field_count); + } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) { + field_count = struct_type->data.structure.src_field_count; + + src_assert(field_count == 0 || struct_type->data.structure.fields != nullptr, decl_node); + } else zig_unreachable(); + + struct_type->data.structure.fields_by_name.init(field_count); + + Scope *scope = &struct_type->data.structure.decls_scope->base; + + size_t gen_field_index = 0; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *type_struct_field = struct_type->data.structure.fields[i]; + + AstNode *field_node; + if (decl_node->type == NodeTypeContainerDecl) { + field_node = decl_node->data.container_decl.fields.at(i); + type_struct_field->name = field_node->data.struct_field.name; + type_struct_field->decl_node = field_node; + if (field_node->data.struct_field.comptime_token != nullptr) { + if (field_node->data.struct_field.value == nullptr) { + add_token_error(g, field_node->owner, + field_node->data.struct_field.comptime_token, + buf_sprintf("comptime struct field missing initialization value")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + type_struct_field->is_comptime = true; + } + + if (field_node->data.struct_field.type == nullptr) { + add_node_error(g, field_node, buf_sprintf("struct field missing type")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) { + field_node = type_struct_field->decl_node; + + src_assert(type_struct_field->type_entry != nullptr, field_node); + } else zig_unreachable(); + + auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field); + if (field_entry != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name))); + add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + ZigValue *field_type_val; + if (decl_node->type == NodeTypeContainerDecl) { + field_type_val = analyze_const_value(g, scope, + field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef); + if (type_is_invalid(field_type_val->type)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + assert(field_type_val->special != ConstValSpecialRuntime); + type_struct_field->type_val = field_type_val; + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) { + field_type_val = type_struct_field->type_val; + } else zig_unreachable(); + + bool field_is_opaque_type; + if ((err = type_val_resolve_is_opaque_type(g, field_type_val, &field_is_opaque_type))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (field_is_opaque_type) { + add_node_error(g, field_node, + buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + type_struct_field->src_index = i; + type_struct_field->gen_index = SIZE_MAX; + + if (type_struct_field->is_comptime) + continue; + + switch (type_val_resolve_requires_comptime(g, field_type_val)) { + case ReqCompTimeYes: + struct_type->data.structure.requires_comptime = true; + break; + case ReqCompTimeInvalid: + if (g->trace_err != nullptr) { + g->trace_err = add_error_note(g, g->trace_err, field_node, + buf_create_from_str("while checking this field")); + } + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + case ReqCompTimeNo: + break; + } + + bool field_is_zero_bits; + if ((err = type_val_resolve_zero_bits(g, field_type_val, struct_type, nullptr, &field_is_zero_bits))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (field_is_zero_bits) + continue; + + type_struct_field->gen_index = gen_field_index; + gen_field_index += 1; + } + + struct_type->data.structure.resolve_loop_flag_zero_bits = false; + struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index; + if (gen_field_index != 0) { + struct_type->abi_size = SIZE_MAX; + struct_type->size_in_bits = SIZE_MAX; + } + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + + struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown; + return ErrorNone; +} + +static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { + assert(struct_type->id == ZigTypeIdStruct); + + Error err; + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown) + return ErrorNone; + if ((err = resolve_struct_zero_bits(g, struct_type))) + return err; + if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown) + return ErrorNone; + + AstNode *decl_node = struct_type->data.structure.decl_node; + + if (struct_type->data.structure.resolve_loop_flag_other) { + if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("struct '%s' depends on itself", buf_ptr(&struct_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + struct_type->data.structure.resolve_loop_flag_other = true; + + size_t field_count = struct_type->data.structure.src_field_count; + bool packed = struct_type->data.structure.layout == ContainerLayoutPacked; + + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + if (field->gen_index == SIZE_MAX) + continue; + + AstNode *align_expr = (field->decl_node->type == NodeTypeStructField) ? + field->decl_node->data.struct_field.align_expr : nullptr; + if (align_expr != nullptr) { + if (!analyze_const_align(g, &struct_type->data.structure.decls_scope->base, align_expr, + &field->align)) + { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } else if (packed) { + field->align = 1; + } else { + if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) { + if (g->trace_err != nullptr) { + g->trace_err = add_error_note(g, g->trace_err, field->decl_node, + buf_create_from_str("while checking this field")); + } + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + return err; + } + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + } + + if (field->align > struct_type->abi_align) { + struct_type->abi_align = field->align; + } + } + + if (!type_has_bits(g, struct_type)) { + assert(struct_type->abi_align == 0); + } + + struct_type->data.structure.resolve_loop_flag_other = false; + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) { + return ErrorSemanticAnalyzeFail; + } + + struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown; + return ErrorNone; +} + +static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { + assert(union_type->id == ZigTypeIdUnion); + + Error err; + + if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + + if (union_type->data.unionation.resolve_status >= ResolveStatusZeroBitsKnown) + return ErrorNone; + + AstNode *decl_node = union_type->data.unionation.decl_node; + + if (union_type->data.unionation.resolve_loop_flag_zero_bits) { + if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("union '%s' depends on itself", + buf_ptr(&union_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + union_type->data.unionation.resolve_loop_flag_zero_bits = true; + + uint32_t field_count; + if (decl_node->type == NodeTypeContainerDecl) { + assert(union_type->data.unionation.fields == nullptr); + field_count = (uint32_t)decl_node->data.container_decl.fields.length; + union_type->data.unionation.src_field_count = field_count; + union_type->data.unionation.fields = heap::c_allocator.allocate(field_count); + union_type->data.unionation.fields_by_name.init(field_count); + } else { + field_count = union_type->data.unionation.src_field_count; + assert(field_count == 0 || union_type->data.unionation.fields != nullptr); + } + + if (field_count == 0) { + add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields")); + union_type->data.unionation.src_field_count = field_count; + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + Scope *scope = &union_type->data.unionation.decls_scope->base; + + HashMap occupied_tag_values = {}; + + bool is_auto_enum; // union(enum) or union(enum(expr)) + bool is_explicit_enum; // union(expr) + AstNode *enum_type_node; // expr in union(enum(expr)) or union(expr) + if (decl_node->type == NodeTypeContainerDecl) { + is_auto_enum = decl_node->data.container_decl.auto_enum; + is_explicit_enum = decl_node->data.container_decl.init_arg_expr != nullptr; + enum_type_node = decl_node->data.container_decl.init_arg_expr; + } else { + is_auto_enum = false; + is_explicit_enum = union_type->data.unionation.tag_type != nullptr; + enum_type_node = nullptr; + } + union_type->data.unionation.have_explicit_tag_type = is_auto_enum || is_explicit_enum; + + bool is_auto_layout = union_type->data.unionation.layout == ContainerLayoutAuto; + bool want_safety = (field_count >= 2) + && (is_auto_layout || is_explicit_enum) + && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease); + ZigType *tag_type; + bool create_enum_type = is_auto_enum || (!is_explicit_enum && want_safety); + bool *covered_enum_fields; + bool *is_zero_bits = heap::c_allocator.allocate(field_count); + ZigLLVMDIEnumerator **di_enumerators; + if (create_enum_type) { + occupied_tag_values.init(field_count); + + di_enumerators = heap::c_allocator.allocate(field_count); + + ZigType *tag_int_type; + if (enum_type_node != nullptr) { + tag_int_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(tag_int_type)) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (tag_int_type->id != ZigTypeIdInt && tag_int_type->id != ZigTypeIdComptimeInt) { + add_node_error(g, enum_type_node, + buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name))); + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } else { + tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + } + + tag_type = new_type_table_entry(ZigTypeIdEnum); + buf_resize(&tag_type->name, 0); + buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name)); + tag_type->llvm_type = tag_int_type->llvm_type; + tag_type->llvm_di_type = tag_int_type->llvm_di_type; + tag_type->abi_size = tag_int_type->abi_size; + tag_type->abi_align = tag_int_type->abi_align; + tag_type->size_in_bits = tag_int_type->size_in_bits; + + tag_type->data.enumeration.tag_int_type = tag_int_type; + tag_type->data.enumeration.resolve_status = ResolveStatusSizeKnown; + tag_type->data.enumeration.decl_node = decl_node; + tag_type->data.enumeration.layout = ContainerLayoutAuto; + tag_type->data.enumeration.src_field_count = field_count; + tag_type->data.enumeration.fields = heap::c_allocator.allocate(field_count); + tag_type->data.enumeration.fields_by_name.init(field_count); + tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope; + } else if (enum_type_node != nullptr) { + ZigType *enum_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(enum_type)) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (enum_type->id != ZigTypeIdEnum) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + add_node_error(g, enum_type_node, + buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); + return ErrorSemanticAnalyzeFail; + } + if ((err = type_resolve(g, enum_type, ResolveStatusAlignmentKnown))) { + assert(g->errors.length != 0); + return err; + } + tag_type = enum_type; + } else { + if (decl_node->type == NodeTypeContainerDecl) { + tag_type = nullptr; + } else { + tag_type = union_type->data.unionation.tag_type; + } + } + if (tag_type != nullptr) { + covered_enum_fields = heap::c_allocator.allocate(tag_type->data.enumeration.src_field_count); + } + union_type->data.unionation.tag_type = tag_type; + + for (uint32_t i = 0; i < field_count; i += 1) { + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + if (decl_node->type == NodeTypeContainerDecl) { + AstNode *field_node = decl_node->data.container_decl.fields.at(i); + union_field->name = field_node->data.struct_field.name; + union_field->decl_node = field_node; + union_field->gen_index = UINT32_MAX; + is_zero_bits[i] = false; + + auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field); + if (field_entry != nullptr) { + ErrorMsg *msg = add_node_error(g, union_field->decl_node, + buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name))); + add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + if (field_node->data.struct_field.type == nullptr) { + if (is_auto_enum || is_explicit_enum) { + union_field->type_entry = g->builtin_types.entry_void; + is_zero_bits[i] = true; + } else { + add_node_error(g, field_node, buf_sprintf("union field missing type")); + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } else { + ZigValue *field_type_val = analyze_const_value(g, scope, + field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef); + if (type_is_invalid(field_type_val->type)) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + assert(field_type_val->special != ConstValSpecialRuntime); + union_field->type_val = field_type_val; + } + + if (field_node->data.struct_field.value != nullptr && !is_auto_enum) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, + buf_create_from_str("untagged union field assignment")); + add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here")); + } + } + + if (union_field->type_val != nullptr) { + bool field_is_opaque_type; + if ((err = type_val_resolve_is_opaque_type(g, union_field->type_val, &field_is_opaque_type))) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + if (field_is_opaque_type) { + add_node_error(g, union_field->decl_node, + buf_create_from_str( + "opaque types have unknown size and therefore cannot be directly embedded in unions")); + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + switch (type_val_resolve_requires_comptime(g, union_field->type_val)) { + case ReqCompTimeInvalid: + if (g->trace_err != nullptr) { + g->trace_err = add_error_note(g, g->trace_err, union_field->decl_node, + buf_create_from_str("while checking this field")); + } + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + case ReqCompTimeYes: + union_type->data.unionation.requires_comptime = true; + break; + case ReqCompTimeNo: + break; + } + + if ((err = type_val_resolve_zero_bits(g, union_field->type_val, union_type, nullptr, &is_zero_bits[i]))) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } + + if (create_enum_type) { + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(union_field->name), i); + union_field->enum_field = &tag_type->data.enumeration.fields[i]; + union_field->enum_field->name = union_field->name; + union_field->enum_field->decl_index = i; + union_field->enum_field->decl_node = union_field->decl_node; + + auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field); + assert(prev_entry == nullptr); // caught by union de-duplicator above + + AstNode *tag_value = decl_node->type == NodeTypeContainerDecl + ? union_field->decl_node->data.struct_field.value : nullptr; + + // In this first pass we resolve explicit tag values. + // In a second pass we will fill in the unspecified ones. + if (tag_value != nullptr) { + ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type; + ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, + nullptr, UndefBad); + if (type_is_invalid(result->type)) { + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); + if (entry == nullptr) { + bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint); + } else { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); + + ErrorMsg *msg = add_node_error(g, tag_value, + buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); + add_error_note(g, msg, entry->value, + buf_sprintf("other occurrence here")); + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } + } else if (tag_type != nullptr) { + union_field->enum_field = find_enum_type_field(tag_type, union_field->name); + if (union_field->enum_field == nullptr) { + ErrorMsg *msg = add_node_error(g, union_field->decl_node, + buf_sprintf("enum field not found: '%s'", buf_ptr(union_field->name))); + add_error_note(g, msg, tag_type->data.enumeration.decl_node, + buf_sprintf("enum declared here")); + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + covered_enum_fields[union_field->enum_field->decl_index] = true; + } else { + union_field->enum_field = heap::c_allocator.create(); + union_field->enum_field->name = union_field->name; + union_field->enum_field->decl_index = i; + bigint_init_unsigned(&union_field->enum_field->value, i); + } + assert(union_field->enum_field != nullptr); + } + + uint32_t gen_field_index = 0; + for (uint32_t i = 0; i < field_count; i += 1) { + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + if (!is_zero_bits[i]) { + union_field->gen_index = gen_field_index; + gen_field_index += 1; + } + } + + bool src_have_tag = is_auto_enum || is_explicit_enum; + + if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) { + const char *qual_str; + switch (union_type->data.unionation.layout) { + case ContainerLayoutAuto: + zig_unreachable(); + case ContainerLayoutPacked: + qual_str = "packed"; + break; + case ContainerLayoutExtern: + qual_str = "extern"; + break; + } + AstNode *source_node = enum_type_node != nullptr ? enum_type_node : decl_node; + add_node_error(g, source_node, + buf_sprintf("%s union does not support enum tag type", qual_str)); + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + + if (create_enum_type) { + if (decl_node->type == NodeTypeContainerDecl) { + // Now iterate again and populate the unspecified tag values + uint32_t next_maybe_unoccupied_index = 0; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeUnionField *union_field = &union_type->data.unionation.fields[field_i]; + AstNode *tag_value = field_node->data.struct_field.value; + + if (tag_value == nullptr) { + if (occupied_tag_values.size() == 0) { + bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + } else { + BigInt proposed_value; + for (;;) { + bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + auto entry = occupied_tag_values.put_unique(proposed_value, field_node); + if (entry != nullptr) { + continue; + } + break; + } + bigint_init_bigint(&union_field->enum_field->value, &proposed_value); + } + } + } + } + } else if (tag_type != nullptr) { + for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; + if (!covered_enum_fields[i]) { + ErrorMsg *msg = add_node_error(g, decl_node, + buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name))); + if (decl_node->type == NodeTypeContainerDecl) { + AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; + AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); + add_error_note(g, msg, field_node, + buf_sprintf("declared here")); + } + union_type->data.unionation.resolve_status = ResolveStatusInvalid; + } + } + } + + if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) { + return ErrorSemanticAnalyzeFail; + } + + union_type->data.unionation.resolve_loop_flag_zero_bits = false; + + union_type->data.unionation.gen_field_count = gen_field_index; + bool zero_bits = gen_field_index == 0 && (field_count < 2 || !src_have_tag); + if (!zero_bits) { + union_type->abi_size = SIZE_MAX; + union_type->size_in_bits = SIZE_MAX; + } + union_type->data.unionation.resolve_status = zero_bits ? ResolveStatusSizeKnown : ResolveStatusZeroBitsKnown; + + return ErrorNone; +} + +void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type) { + if (g->root_import == container_type || buf_len(&container_type->name) == 0) return; + buf_append_buf(buf, &container_type->name); + buf_append_char(buf, NAMESPACE_SEP_CHAR); +} + +static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool is_test) { + buf_resize(buf, 0); + + Scope *scope = tld->parent_scope; + while (scope->id != ScopeIdDecls) { + scope = scope->parent; + } + ScopeDecls *decls_scope = reinterpret_cast(scope); + append_namespace_qualification(g, buf, decls_scope->container_type); + if (is_test) { + buf_append_str(buf, "test \""); + buf_append_buf(buf, tld->name); + buf_append_char(buf, '"'); + } else { + buf_append_buf(buf, tld->name); + } +} + +static ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) { + ZigFn *fn_entry = heap::c_allocator.create(); + fn_entry->ir_executable = heap::c_allocator.create(); + + fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota; + + fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc; + fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota; + fn_entry->analyzed_executable.fn_entry = fn_entry; + fn_entry->ir_executable->fn_entry = fn_entry; + fn_entry->fn_inline = inline_value; + + return fn_entry; +} + +ZigFn *create_fn(CodeGen *g, AstNode *proto_node) { + assert(proto_node->type == NodeTypeFnProto); + AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; + + ZigFn *fn_entry = create_fn_raw(g, fn_proto->fn_inline); + + fn_entry->proto_node = proto_node; + fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr : + proto_node->data.fn_proto.fn_def_node->data.fn_def.body; + + fn_entry->analyzed_executable.source_node = fn_entry->body_node; + + return fn_entry; +} + +ZigType *get_test_fn_type(CodeGen *g) { + if (g->test_fn_type) + return g->test_fn_type; + + FnTypeId fn_type_id = {0}; + fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set, + g->builtin_types.entry_void); + g->test_fn_type = get_fn_type(g, &fn_type_id); + return g->test_fn_type; +} + +void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage) { + GlobalExport *global_export = var->export_list.add_one(); + memset(global_export, 0, sizeof(GlobalExport)); + buf_init_from_str(&global_export->name, symbol_name); + global_export->linkage = linkage; +} + +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) { + if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->link_libc) { + g->have_c_main = true; + } else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) { + if (strcmp(symbol_name, "WinMain") == 0) { + g->have_winmain = true; + } else if (strcmp(symbol_name, "wWinMain") == 0) { + g->have_wwinmain = true; + } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) { + g->have_winmain_crt_startup = true; + } else if (strcmp(symbol_name, "wWinMainCRTStartup") == 0) { + g->have_wwinmain_crt_startup = true; + } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) { + g->have_dllmain_crt_startup = true; + } + } + + GlobalExport *fn_export = fn_table_entry->export_list.add_one(); + memset(fn_export, 0, sizeof(GlobalExport)); + buf_init_from_str(&fn_export->name, symbol_name); + fn_export->linkage = linkage; +} + +static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { + AstNode *source_node = tld_fn->base.source_node; + if (source_node->type == NodeTypeFnProto) { + AstNodeFnProto *fn_proto = &source_node->data.fn_proto; + + AstNode *fn_def_node = fn_proto->fn_def_node; + + ZigFn *fn_table_entry = create_fn(g, source_node); + tld_fn->fn_entry = fn_table_entry; + + bool is_extern = (fn_table_entry->body_node == nullptr); + if (fn_proto->is_export || is_extern) { + buf_init_from_buf(&fn_table_entry->symbol_name, tld_fn->base.name); + } else { + get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, false); + } + + if (!is_extern) { + fn_table_entry->fndef_scope = create_fndef_scope(g, + fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry); + + for (size_t i = 0; i < fn_proto->params.length; i += 1) { + AstNode *param_node = fn_proto->params.at(i); + assert(param_node->type == NodeTypeParamDecl); + if (param_node->data.param_decl.name == nullptr) { + add_node_error(g, param_node, buf_sprintf("missing parameter name")); + } + } + } else { + fn_table_entry->inferred_async_node = inferred_async_none; + g->external_symbol_names.put_unique(tld_fn->base.name, &tld_fn->base); + } + + Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; + + CallingConvention cc; + if (fn_proto->callconv_expr != nullptr) { + ZigType *cc_enum_value = get_builtin_type(g, "CallingConvention"); + + ZigValue *result_val = analyze_const_value(g, child_scope, fn_proto->callconv_expr, + cc_enum_value, nullptr, UndefBad); + if (type_is_invalid(result_val->type)) { + fn_table_entry->type_entry = g->builtin_types.entry_invalid; + tld_fn->base.resolution = TldResolutionInvalid; + return; + } + + cc = (CallingConvention)bigint_as_u32(&result_val->data.x_enum_tag); + } else { + cc = cc_from_fn_proto(fn_proto); + } + + if (fn_proto->section_expr != nullptr) { + if (!analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name)) { + fn_table_entry->type_entry = g->builtin_types.entry_invalid; + tld_fn->base.resolution = TldResolutionInvalid; + return; + } + } + + fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry, cc); + + if (type_is_invalid(fn_table_entry->type_entry)) { + tld_fn->base.resolution = TldResolutionInvalid; + return; + } + + const CallingConvention fn_cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; + + if (fn_proto->is_export) { + switch (fn_cc) { + case CallingConventionAsync: + add_node_error(g, fn_def_node, + buf_sprintf("exported function cannot be async")); + fn_table_entry->type_entry = g->builtin_types.entry_invalid; + tld_fn->base.resolution = TldResolutionInvalid; + return; + case CallingConventionC: + case CallingConventionCold: + case CallingConventionNaked: + case CallingConventionInterrupt: + case CallingConventionSignal: + case CallingConventionStdcall: + case CallingConventionFastcall: + case CallingConventionVectorcall: + case CallingConventionThiscall: + case CallingConventionAPCS: + case CallingConventionAAPCS: + case CallingConventionAAPCSVFP: + add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), + GlobalLinkageIdStrong, fn_cc); + break; + case CallingConventionUnspecified: + // An exported function without a specific calling + // convention defaults to C + add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), + GlobalLinkageIdStrong, CallingConventionC); + break; + } + } + + if (!fn_table_entry->type_entry->data.fn.is_generic) { + if (fn_def_node) + g->fn_defs.append(fn_table_entry); + } + + // if the calling convention implies that it cannot be async, we save that for later + // and leave the value to be nullptr to indicate that we have not emitted possible + // compile errors for improperly calling async functions. + if (fn_cc == CallingConventionAsync) { + fn_table_entry->inferred_async_node = fn_table_entry->proto_node; + } + } else if (source_node->type == NodeTypeTestDecl) { + ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto); + + get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, true); + + tld_fn->fn_entry = fn_table_entry; + + fn_table_entry->proto_node = source_node; + fn_table_entry->fndef_scope = create_fndef_scope(g, source_node, tld_fn->base.parent_scope, fn_table_entry); + fn_table_entry->type_entry = get_test_fn_type(g); + fn_table_entry->body_node = source_node->data.test_decl.body; + fn_table_entry->is_test = true; + + g->fn_defs.append(fn_table_entry); + g->test_fns.append(fn_table_entry); + + } else { + zig_unreachable(); + } +} + +static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) { + assert(tld_comptime->base.source_node->type == NodeTypeCompTime); + AstNode *expr_node = tld_comptime->base.source_node->data.comptime_expr.expr; + analyze_const_value(g, tld_comptime->base.parent_scope, expr_node, g->builtin_types.entry_void, + nullptr, UndefBad); +} + +static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { + bool is_export = false; + if (tld->id == TldIdVar) { + assert(tld->source_node->type == NodeTypeVariableDeclaration); + is_export = tld->source_node->data.variable_declaration.is_export; + } else if (tld->id == TldIdFn) { + assert(tld->source_node->type == NodeTypeFnProto); + is_export = tld->source_node->data.fn_proto.is_export; + + if (!tld->source_node->data.fn_proto.is_extern && + tld->source_node->data.fn_proto.fn_def_node == nullptr) + { + add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body")); + return; + } + if (!tld->source_node->data.fn_proto.is_extern && + tld->source_node->data.fn_proto.is_var_args) + { + add_node_error(g, tld->source_node, buf_sprintf("non-extern function is variadic")); + return; + } + } else if (tld->id == TldIdUsingNamespace) { + g->resolve_queue.append(tld); + } + if (is_export) { + g->resolve_queue.append(tld); + + auto entry = g->exported_symbol_names.put_unique(tld->name, tld); + if (entry) { + AstNode *other_source_node = entry->value->source_node; + ErrorMsg *msg = add_node_error(g, tld->source_node, + buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name))); + add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here")); + } + } + + if (tld->name != nullptr) { + auto entry = decls_scope->decl_table.put_unique(tld->name, tld); + if (entry) { + Tld *other_tld = entry->value; + if (other_tld->id == TldIdVar) { + ZigVar *var = reinterpret_cast(other_tld)->var; + if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) { + return; // already reported compile error + } + } + 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; + } + + ZigType *type; + if (get_primitive_type(g, tld->name, &type) != ErrorPrimitiveTypeNotFound) { + add_node_error(g, tld->source_node, + buf_sprintf("declaration shadows primitive type '%s'", buf_ptr(tld->name))); + } + } +} + +static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) { + assert(node->type == NodeTypeTestDecl); + + if (!g->is_test_build) + return; + + ZigType *import = get_scope_import(&decls_scope->base); + if (import->data.structure.root_struct->package != g->main_pkg) + return; + + Buf *decl_name_buf = node->data.test_decl.name; + + Buf *test_name = g->test_name_prefix ? + buf_sprintf("%s%s", buf_ptr(g->test_name_prefix), buf_ptr(decl_name_buf)) : decl_name_buf; + + if (g->test_filter != nullptr && strstr(buf_ptr(test_name), buf_ptr(g->test_filter)) == nullptr) { + return; + } + + TldFn *tld_fn = heap::c_allocator.create(); + init_tld(&tld_fn->base, TldIdFn, test_name, VisibModPrivate, node, &decls_scope->base); + g->resolve_queue.append(&tld_fn->base); +} + +static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) { + assert(node->type == NodeTypeCompTime); + + TldCompTime *tld_comptime = heap::c_allocator.create(); + init_tld(&tld_comptime->base, TldIdCompTime, nullptr, VisibModPrivate, node, &decls_scope->base); + g->resolve_queue.append(&tld_comptime->base); +} + +void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, + Scope *parent_scope) +{ + tld->id = id; + tld->name = name; + tld->visib_mod = visib_mod; + tld->source_node = source_node; + tld->import = source_node ? source_node->owner : nullptr; + tld->parent_scope = parent_scope; +} + +void update_compile_var(CodeGen *g, Buf *name, ZigValue *value) { + ScopeDecls *builtin_scope = get_container_scope(g->compile_var_import); + Tld *tld = find_container_decl(g, builtin_scope, name); + assert(tld != nullptr); + resolve_top_level_decl(g, tld, tld->source_node, false); + assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk); + TldVar *tld_var = (TldVar *)tld; + copy_const_val(g, tld_var->var->const_value, value); + tld_var->var->var_type = value->type; + tld_var->var->align_bytes = get_abi_alignment(g, value->type); +} + +void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { + switch (node->type) { + case NodeTypeContainerDecl: + for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) { + AstNode *child = node->data.container_decl.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 = heap::c_allocator.create(); + init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base); + tld_var->extern_lib_name = node->data.variable_declaration.lib_name; + add_top_level_decl(g, decls_scope, &tld_var->base); + break; + } + case NodeTypeFnProto: + { + // if the name is missing, we immediately announce an error + Buf *fn_name = node->data.fn_proto.name; + if (fn_name == nullptr) { + add_node_error(g, node, buf_sprintf("missing function name")); + break; + } + + VisibMod visib_mod = node->data.fn_proto.visib_mod; + TldFn *tld_fn = heap::c_allocator.create(); + init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base); + tld_fn->extern_lib_name = node->data.fn_proto.lib_name; + add_top_level_decl(g, decls_scope, &tld_fn->base); + + break; + } + case NodeTypeUsingNamespace: { + VisibMod visib_mod = node->data.using_namespace.visib_mod; + TldUsingNamespace *tld_using_namespace = heap::c_allocator.create(); + init_tld(&tld_using_namespace->base, TldIdUsingNamespace, nullptr, visib_mod, node, &decls_scope->base); + add_top_level_decl(g, decls_scope, &tld_using_namespace->base); + decls_scope->use_decls.append(tld_using_namespace); + break; + } + case NodeTypeTestDecl: + preview_test_decl(g, node, decls_scope); + break; + case NodeTypeCompTime: + preview_comptime_decl(g, node, decls_scope); + break; + case NodeTypeNoSuspend: + case NodeTypeParamDecl: + case NodeTypeReturnExpr: + case NodeTypeDefer: + case NodeTypeBlock: + case NodeTypeGroupedExpr: + case NodeTypeBinOpExpr: + case NodeTypeCatchExpr: + case NodeTypeFnCallExpr: + case NodeTypeArrayAccessExpr: + case NodeTypeSliceExpr: + case NodeTypeFloatLiteral: + case NodeTypeIntLiteral: + case NodeTypeStringLiteral: + case NodeTypeCharLiteral: + case NodeTypeBoolLiteral: + case NodeTypeNullLiteral: + case NodeTypeUndefinedLiteral: + case NodeTypeSymbol: + case NodeTypePrefixOpExpr: + case NodeTypePointerType: + case NodeTypeIfBoolExpr: + case NodeTypeWhileExpr: + case NodeTypeForExpr: + case NodeTypeSwitchExpr: + case NodeTypeSwitchProng: + case NodeTypeSwitchRange: + case NodeTypeBreak: + case NodeTypeContinue: + case NodeTypeUnreachable: + case NodeTypeAsmExpr: + case NodeTypeFieldAccessExpr: + case NodeTypePtrDeref: + case NodeTypeUnwrapOptional: + case NodeTypeStructField: + case NodeTypeContainerInitExpr: + case NodeTypeStructValueField: + case NodeTypeArrayType: + case NodeTypeInferredArrayType: + case NodeTypeErrorType: + case NodeTypeIfErrorExpr: + case NodeTypeIfOptional: + case NodeTypeErrorSetDecl: + case NodeTypeResume: + case NodeTypeAwaitExpr: + case NodeTypeSuspend: + case NodeTypeEnumLiteral: + case NodeTypeAnyFrameType: + case NodeTypeErrorSetField: + case NodeTypeAnyTypeField: + zig_unreachable(); + } +} + +static Error resolve_decl_container(CodeGen *g, TldContainer *tld_container) { + ZigType *type_entry = tld_container->type_entry; + assert(type_entry); + + switch (type_entry->id) { + case ZigTypeIdStruct: + return resolve_struct_type(g, tld_container->type_entry); + case ZigTypeIdEnum: + return resolve_enum_zero_bits(g, tld_container->type_entry); + case ZigTypeIdUnion: + return resolve_union_type(g, tld_container->type_entry); + default: + zig_unreachable(); + } +} + +ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry) { + switch (type_entry->id) { + case ZigTypeIdInvalid: + return g->builtin_types.entry_invalid; + case ZigTypeIdOpaque: + if (source_node->is_extern) + return type_entry; + ZIG_FALLTHROUGH; + case ZigTypeIdUnreachable: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + add_node_error(g, source_node->type, buf_sprintf("variable of type '%s' not allowed", + buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + 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 +ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, + bool is_const, ZigValue *const_value, Tld *src_tld, ZigType *var_type) +{ + Error err; + assert(const_value != nullptr); + assert(var_type != nullptr); + + ZigVar *variable_entry = heap::c_allocator.create(); + variable_entry->const_value = const_value; + variable_entry->var_type = var_type; + variable_entry->parent_scope = parent_scope; + variable_entry->shadowable = false; + variable_entry->src_arg_index = SIZE_MAX; + + assert(name); + variable_entry->name = strdup(buf_ptr(name)); + + if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) { + variable_entry->var_type = g->builtin_types.entry_invalid; + } else { + variable_entry->align_bytes = get_abi_alignment(g, var_type); + + ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr); + if (existing_var && !existing_var->shadowable) { + if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) { + 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->var_type = g->builtin_types.entry_invalid; + } else { + ZigType *type; + if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) { + add_node_error(g, source_node, + buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); + variable_entry->var_type = g->builtin_types.entry_invalid; + } else { + Scope *search_scope = nullptr; + if (src_tld == nullptr) { + search_scope = parent_scope; + } else if (src_tld->parent_scope != nullptr && src_tld->parent_scope->parent != nullptr) { + search_scope = src_tld->parent_scope->parent; + } + if (search_scope != nullptr) { + Tld *tld = find_decl(g, search_scope, name); + if (tld != nullptr && tld != src_tld) { + bool want_err_msg = true; + if (tld->id == TldIdVar) { + ZigVar *var = reinterpret_cast(tld)->var; + if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) { + want_err_msg = false; + } + } + if (want_err_msg) { + 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->var_type = g->builtin_types.entry_invalid; + } + } + } + } + } + + Scope *child_scope; + if (source_node && source_node->type == NodeTypeParamDecl) { + child_scope = create_var_scope(g, 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 validate_export_var_type(CodeGen *g, ZigType* type, AstNode *source_node) { + switch (type->id) { + case ZigTypeIdMetaType: + add_node_error(g, source_node, buf_sprintf("cannot export variable of type 'type'")); + break; + default: + break; + } +} + +static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { + AstNode *source_node = tld_var->base.source_node; + AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration; + + bool is_const = var_decl->is_const; + bool is_extern = var_decl->is_extern; + bool is_export = var_decl->is_export; + bool is_thread_local = var_decl->threadlocal_tok != nullptr; + + ZigType *explicit_type = nullptr; + if (var_decl->type) { + if (tld_var->analyzing_type) { + add_node_error(g, var_decl->type, + buf_sprintf("type of '%s' depends on itself", buf_ptr(tld_var->base.name))); + explicit_type = g->builtin_types.entry_invalid; + } else { + tld_var->analyzing_type = true; + ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type); + explicit_type = validate_var_type(g, var_decl, proposed_type); + } + } + + assert(!is_export || !is_extern); + + ZigValue *init_value = nullptr; + + // TODO more validation for types that can't be used for export/extern variables + ZigType *implicit_type = nullptr; + if (explicit_type != nullptr && type_is_invalid(explicit_type)) { + 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, allow_lazy ? LazyOk : UndefOk); + assert(init_value); + implicit_type = init_value->type; + + if (implicit_type->id == ZigTypeIdUnreachable) { + add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); + implicit_type = g->builtin_types.entry_invalid; + } else if ((!is_const || is_extern) && + (implicit_type->id == ZigTypeIdComptimeFloat || + implicit_type->id == ZigTypeIdComptimeInt || + implicit_type->id == ZigTypeIdEnumLiteral)) + { + 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 == ZigTypeIdNull) { + 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 == ZigTypeIdMetaType && !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 == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime); + } else if (!is_extern) { + add_node_error(g, source_node, buf_sprintf("variables must be initialized")); + implicit_type = g->builtin_types.entry_invalid; + } else if (explicit_type == nullptr) { + // extern variable without explicit type + add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); + implicit_type = g->builtin_types.entry_invalid; + } + + ZigType *type = explicit_type ? explicit_type : implicit_type; + assert(type != nullptr); // should have been caught by the parser + + ZigValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(g, type); + + tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, + is_const, init_val, &tld_var->base, type); + tld_var->var->is_thread_local = is_thread_local; + + if (implicit_type != nullptr && type_is_invalid(implicit_type)) { + tld_var->var->var_type = g->builtin_types.entry_invalid; + } + + if (var_decl->align_expr != nullptr) { + if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) { + tld_var->var->var_type = g->builtin_types.entry_invalid; + } + } + + if (var_decl->section_expr != nullptr) { + if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->var->section_name)) { + tld_var->var->section_name = nullptr; + } + } + + if (is_thread_local && is_const) { + add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant")); + } + + if (is_export) { + validate_export_var_type(g, type, source_node); + add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong); + } + + if (is_extern) { + g->external_symbol_names.put_unique(tld_var->base.name, &tld_var->base); + } + + g->global_vars.append(tld_var); +} + +static void add_symbols_from_container(CodeGen *g, TldUsingNamespace *src_using_namespace, + TldUsingNamespace *dst_using_namespace, ScopeDecls* dest_decls_scope) +{ + if (src_using_namespace->base.resolution == TldResolutionUnresolved || + src_using_namespace->base.resolution == TldResolutionResolving) + { + assert(src_using_namespace->base.parent_scope->id == ScopeIdDecls); + ScopeDecls *src_decls_scope = (ScopeDecls *)src_using_namespace->base.parent_scope; + preview_use_decl(g, src_using_namespace, src_decls_scope); + if (src_using_namespace != dst_using_namespace) { + resolve_use_decl(g, src_using_namespace, src_decls_scope); + } + } + + ZigValue *use_expr = src_using_namespace->using_namespace_value; + if (type_is_invalid(use_expr->type)) { + dest_decls_scope->any_imports_failed = true; + return; + } + + dst_using_namespace->base.resolution = TldResolutionOk; + + assert(use_expr->special != ConstValSpecialRuntime); + + // The source scope for the imported symbols + ScopeDecls *src_scope = get_container_scope(use_expr->data.x_type); + // The top-level container where the symbols are defined, it's used in the + // loop below in order to exclude the ones coming from an import statement + ZigType *src_import = get_scope_import(&src_scope->base); + assert(src_import != nullptr); + + if (src_scope->any_imports_failed) { + dest_decls_scope->any_imports_failed = true; + } + + auto it = src_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Buf *target_tld_name = entry->key; + Tld *target_tld = entry->value; + + if (target_tld->visib_mod == VisibModPrivate) { + continue; + } + + if (target_tld->import != src_import) { + continue; + } + + auto existing_entry = dest_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_using_namespace->base.source_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 < src_scope->use_decls.length; i += 1) { + TldUsingNamespace *tld_using_namespace = src_scope->use_decls.at(i); + if (tld_using_namespace->base.visib_mod != VisibModPrivate) + add_symbols_from_container(g, tld_using_namespace, dst_using_namespace, dest_decls_scope); + } +} + +static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope) { + if (tld_using_namespace->base.resolution == TldResolutionOk || + tld_using_namespace->base.resolution == TldResolutionInvalid) + { + return; + } + add_symbols_from_container(g, tld_using_namespace, tld_using_namespace, dest_decls_scope); +} + +static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope) { + if (using_namespace->base.resolution == TldResolutionOk || + using_namespace->base.resolution == TldResolutionInvalid || + using_namespace->using_namespace_value != nullptr) + { + return; + } + + using_namespace->base.resolution = TldResolutionResolving; + assert(using_namespace->base.source_node->type == NodeTypeUsingNamespace); + ZigValue *result = analyze_const_value(g, &dest_decls_scope->base, + using_namespace->base.source_node->data.using_namespace.expr, g->builtin_types.entry_type, + nullptr, UndefBad); + using_namespace->using_namespace_value = result; + + if (type_is_invalid(result->type)) { + dest_decls_scope->any_imports_failed = true; + using_namespace->base.resolution = TldResolutionInvalid; + using_namespace->using_namespace_value = g->invalid_inst_gen->value; + return; + } + + if (!is_container(result->data.x_type)) { + add_node_error(g, using_namespace->base.source_node, + buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&result->data.x_type->name))); + dest_decls_scope->any_imports_failed = true; + using_namespace->base.resolution = TldResolutionInvalid; + using_namespace->using_namespace_value = g->invalid_inst_gen->value; + return; + } +} + +void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool allow_lazy) { + bool want_resolve_lazy = tld->resolution == TldResolutionOkLazy && !allow_lazy; + if (tld->resolution != TldResolutionUnresolved && !want_resolve_lazy) + return; + + tld->resolution = TldResolutionResolving; + update_progress_display(g); + + switch (tld->id) { + case TldIdVar: { + TldVar *tld_var = (TldVar *)tld; + if (want_resolve_lazy) { + ir_resolve_lazy(g, source_node, tld_var->var->const_value); + } else { + resolve_decl_var(g, tld_var, allow_lazy); + } + tld->resolution = allow_lazy ? TldResolutionOkLazy : TldResolutionOk; + break; + } + case TldIdFn: { + TldFn *tld_fn = (TldFn *)tld; + resolve_decl_fn(g, tld_fn); + + tld->resolution = TldResolutionOk; + break; + } + case TldIdContainer: { + TldContainer *tld_container = (TldContainer *)tld; + resolve_decl_container(g, tld_container); + + tld->resolution = TldResolutionOk; + break; + } + case TldIdCompTime: { + TldCompTime *tld_comptime = (TldCompTime *)tld; + resolve_decl_comptime(g, tld_comptime); + + tld->resolution = TldResolutionOk; + break; + } + case TldIdUsingNamespace: { + TldUsingNamespace *tld_using_namespace = (TldUsingNamespace *)tld; + assert(tld_using_namespace->base.parent_scope->id == ScopeIdDecls); + ScopeDecls *dest_decls_scope = (ScopeDecls *)tld_using_namespace->base.parent_scope; + preview_use_decl(g, tld_using_namespace, dest_decls_scope); + resolve_use_decl(g, tld_using_namespace, dest_decls_scope); + + tld->resolution = TldResolutionOk; + break; + } + } + + if (g->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) { + g->trace_err = add_error_note(g, g->trace_err, source_node, buf_create_from_str("referenced here")); + source_node->already_traced_this_node = true; + } +} + +Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name) { + // resolve all the using_namespace decls + for (size_t i = 0; i < decls_scope->use_decls.length; i += 1) { + TldUsingNamespace *tld_using_namespace = decls_scope->use_decls.at(i); + if (tld_using_namespace->base.resolution == TldResolutionUnresolved) { + preview_use_decl(g, tld_using_namespace, decls_scope); + resolve_use_decl(g, tld_using_namespace, decls_scope); + } + } + + auto entry = decls_scope->decl_table.maybe_get(name); + return (entry == nullptr) ? nullptr : entry->value; +} + +Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) { + while (scope) { + if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + + Tld *result = find_container_decl(g, decls_scope, name); + if (result != nullptr) + return result; + } + scope = scope->parent; + } + return nullptr; +} + +ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name, ScopeFnDef **crossed_fndef_scope) { + ScopeFnDef *my_crossed_fndef_scope = nullptr; + while (scope) { + if (scope->id == ScopeIdVarDecl) { + ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; + if (buf_eql_str(name, var_scope->var->name)) { + if (crossed_fndef_scope != nullptr) + *crossed_fndef_scope = my_crossed_fndef_scope; + 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) { + if (crossed_fndef_scope != nullptr) + *crossed_fndef_scope = nullptr; + return tld_var->var; + } + } + } + } else if (scope->id == ScopeIdFnDef) { + my_crossed_fndef_scope = (ScopeFnDef *)scope; + } + scope = scope->parent; + } + + return nullptr; +} + +ZigFn *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; +} + +ZigPackage *scope_package(Scope *scope) { + ZigType *import = get_scope_import(scope); + assert(is_top_level_struct(import)); + return import->data.structure.root_struct->package; +} + +TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) { + assert(enum_type->id == ZigTypeIdEnum); + if (enum_type->data.enumeration.src_field_count == 0) + return nullptr; + auto entry = enum_type->data.enumeration.fields_by_name.maybe_get(name); + if (entry == nullptr) + return nullptr; + return entry->value; +} + +TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name) { + assert(type_entry->id == ZigTypeIdStruct); + if (type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) { + for (size_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; + } else { + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); + if (type_entry->data.structure.src_field_count == 0) + return nullptr; + auto entry = type_entry->data.structure.fields_by_name.maybe_get(name); + if (entry == nullptr) + return nullptr; + return entry->value; + } +} + +TypeUnionField *find_union_type_field(ZigType *type_entry, Buf *name) { + assert(type_entry->id == ZigTypeIdUnion); + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); + if (type_entry->data.unionation.src_field_count == 0) + return nullptr; + auto entry = type_entry->data.unionation.fields_by_name.maybe_get(name); + if (entry == nullptr) + return nullptr; + return entry->value; +} + +TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag) { + assert(type_entry->id == ZigTypeIdUnion); + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); + for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { + TypeUnionField *field = &type_entry->data.unionation.fields[i]; + if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) { + return field; + } + } + return nullptr; +} + +TypeEnumField *find_enum_field_by_tag(ZigType *enum_type, const BigInt *tag) { + assert(type_is_resolved(enum_type, ResolveStatusZeroBitsKnown)); + for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *field = &enum_type->data.enumeration.fields[i]; + if (bigint_cmp(&field->value, tag) == CmpEQ) { + return field; + } + } + return nullptr; +} + + +bool is_container(ZigType *type_entry) { + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdStruct: + return type_entry->data.structure.special != StructSpecialSlice; + case ZigTypeIdEnum: + case ZigTypeIdUnion: + return true; + case ZigTypeIdPointer: + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdArray: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + return false; + } + zig_unreachable(); +} + +bool is_ref(ZigType *type_entry) { + return type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle; +} + +bool is_array_ref(ZigType *type_entry) { + ZigType *array = is_ref(type_entry) ? + type_entry->data.pointer.child_type : type_entry; + return array->id == ZigTypeIdArray; +} + +bool is_container_ref(ZigType *parent_ty) { + ZigType *ty = is_ref(parent_ty) ? parent_ty->data.pointer.child_type : parent_ty; + return is_slice(ty) || is_container(ty); +} + +ZigType *container_ref_type(ZigType *type_entry) { + assert(is_container_ref(type_entry)); + return is_ref(type_entry) ? + type_entry->data.pointer.child_type : type_entry; +} + +ZigType *get_src_ptr_type(ZigType *type) { + if (type->id == ZigTypeIdPointer) return type; + if (type->id == ZigTypeIdFn) return type; + if (type->id == ZigTypeIdAnyFrame) return type; + if (type->id == ZigTypeIdOptional) { + if (type->data.maybe.child_type->id == ZigTypeIdPointer) { + return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type; + } + if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type; + if (type->data.maybe.child_type->id == ZigTypeIdAnyFrame) return type->data.maybe.child_type; + } + return nullptr; +} + +Error get_codegen_ptr_type(CodeGen *g, ZigType *type, ZigType **result) { + Error err; + + ZigType *ty = get_src_ptr_type(type); + if (ty == nullptr) { + *result = nullptr; + return ErrorNone; + } + + bool has_bits; + if ((err = type_has_bits2(g, ty, &has_bits))) return err; + if (!has_bits) { + *result = nullptr; + return ErrorNone; + } + + *result = ty; + return ErrorNone; +} + +ZigType *get_codegen_ptr_type_bail(CodeGen *g, ZigType *type) { + Error err; + ZigType *result; + if ((err = get_codegen_ptr_type(g, type, &result))) { + codegen_report_errors_and_exit(g); + } + return result; +} + +bool type_is_nonnull_ptr(CodeGen *g, ZigType *type) { + Error err; + bool result; + if ((err = type_is_nonnull_ptr2(g, type, &result))) { + codegen_report_errors_and_exit(g); + } + return result; +} + +Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) { + Error err; + ZigType *ptr_type; + if ((err = get_codegen_ptr_type(g, type, &ptr_type))) return err; + *result = ptr_type == type && !ptr_allows_addr_zero(type); + return ErrorNone; +} + +static uint32_t get_async_frame_align_bytes(CodeGen *g) { + uint32_t a = g->pointer_size_bytes * 2; + // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw + if (a < 8) a = 8; + return a; +} + +uint32_t get_ptr_align(CodeGen *g, ZigType *type) { + ZigType *ptr_type; + if (type->id == ZigTypeIdStruct) { + assert(type->data.structure.special == StructSpecialSlice); + TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(g, ptr_field); + } else { + ptr_type = get_src_ptr_type(type); + } + if (ptr_type->id == ZigTypeIdPointer) { + return (ptr_type->data.pointer.explicit_alignment == 0) ? + get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment; + } else if (ptr_type->id == ZigTypeIdFn) { + // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM: + // "Cannot getTypeInfo() on a type that is unsized!" + // when getting the alignment of `?fn() callconv(.C) void`. + // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html + return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment; + } else if (ptr_type->id == ZigTypeIdAnyFrame) { + return get_async_frame_align_bytes(g); + } else { + zig_unreachable(); + } +} + +bool get_ptr_const(CodeGen *g, ZigType *type) { + ZigType *ptr_type; + if (type->id == ZigTypeIdStruct) { + assert(type->data.structure.special == StructSpecialSlice); + TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(g, ptr_field); + } else { + ptr_type = get_src_ptr_type(type); + } + if (ptr_type->id == ZigTypeIdPointer) { + return ptr_type->data.pointer.is_const; + } else if (ptr_type->id == ZigTypeIdFn) { + return true; + } else if (ptr_type->id == ZigTypeIdAnyFrame) { + return true; + } else { + zig_unreachable(); + } +} + +AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index) { + if (fn_entry->param_source_nodes) + return fn_entry->param_source_nodes[index]; + else if (fn_entry->proto_node) + return fn_entry->proto_node->data.fn_proto.params.at(index); + else + return nullptr; +} + +static Error define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { + Error err; + ZigType *fn_type = fn_table_entry->type_entry; + assert(!fn_type->data.fn.is_generic); + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + for (size_t i = 0; i < fn_type_id->param_count; i += 1) { + FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; + AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); + Buf *param_name; + bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args; + if (param_decl_node && !is_var_args) { + param_name = param_decl_node->data.param_decl.name; + } else { + param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i); + } + if (param_name == nullptr) { + continue; + } + + ZigType *param_type = param_info->type; + if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) { + return err; + } + + bool is_noalias = param_info->is_noalias; + if (is_noalias) { + ZigType *ptr_type; + if ((err = get_codegen_ptr_type(g, param_type, &ptr_type))) return err; + if (ptr_type == nullptr) { + add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); + } + } + + ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, + param_name, true, create_const_runtime(g, param_type), nullptr, 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(g, param_type)) { + fn_table_entry->variable_list.append(var); + } + } + + return ErrorNone; +} + +bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node) { + assert(err_set_type->id == ZigTypeIdErrorSet); + ZigFn *infer_fn = err_set_type->data.error_set.infer_fn; + if (infer_fn != nullptr && err_set_type->data.error_set.incomplete) { + if (infer_fn->anal_state == FnAnalStateInvalid) { + return false; + } else if (infer_fn->anal_state == FnAnalStateReady) { + analyze_fn_body(g, infer_fn); + if (infer_fn->anal_state == FnAnalStateInvalid || + err_set_type->data.error_set.incomplete) + { + assert(g->errors.length != 0); + return false; + } + } else { + add_node_error(g, source_node, + buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", + buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); + return false; + } + } + return true; +} + +static void resolve_async_fn_frame(CodeGen *g, ZigFn *fn) { + ZigType *frame_type = get_fn_frame_type(g, fn); + Error err; + if ((err = type_resolve(g, frame_type, ResolveStatusSizeKnown))) { + if (g->trace_err != nullptr && frame_type->data.frame.resolve_loop_src_node != nullptr && + !frame_type->data.frame.reported_loop_err) + { + frame_type->data.frame.reported_loop_err = true; + g->trace_err = add_error_note(g, g->trace_err, frame_type->data.frame.resolve_loop_src_node, + buf_sprintf("when analyzing type '%s' here", buf_ptr(&frame_type->name))); + } + fn->anal_state = FnAnalStateInvalid; + return; + } +} + +bool fn_is_async(ZigFn *fn) { + assert(fn->inferred_async_node != nullptr); + assert(fn->inferred_async_node != inferred_async_checking); + return fn->inferred_async_node != inferred_async_none; +} + +void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { + assert(fn->inferred_async_node != nullptr); + assert(fn->inferred_async_node != inferred_async_checking); + assert(fn->inferred_async_node != inferred_async_none); + if (fn->inferred_async_fn != nullptr) { + ErrorMsg *new_msg; + if (fn->inferred_async_node->type == NodeTypeAwaitExpr) { + new_msg = add_error_note(g, msg, fn->inferred_async_node, + buf_create_from_str("await here is a suspend point")); + } else { + new_msg = add_error_note(g, msg, fn->inferred_async_node, + buf_sprintf("async function call here")); + } + return add_async_error_notes(g, new_msg, fn->inferred_async_fn); + } else if (fn->inferred_async_node->type == NodeTypeFnProto) { + add_error_note(g, msg, fn->inferred_async_node, + buf_sprintf("async calling convention here")); + } else if (fn->inferred_async_node->type == NodeTypeSuspend) { + add_error_note(g, msg, fn->inferred_async_node, + buf_sprintf("suspends here")); + } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) { + add_error_note(g, msg, fn->inferred_async_node, + buf_sprintf("await here is a suspend point")); + } else if (fn->inferred_async_node->type == NodeTypeFnCallExpr && + fn->inferred_async_node->data.fn_call_expr.modifier == CallModifierBuiltin) + { + add_error_note(g, msg, fn->inferred_async_node, + buf_sprintf("@frame() causes function to be async")); + } else { + add_error_note(g, msg, fn->inferred_async_node, + buf_sprintf("suspends here")); + } +} + +// ErrorNone - not async +// ErrorIsAsync - yes async +// ErrorSemanticAnalyzeFail - compile error emitted result is invalid +static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode *call_node, + bool must_not_be_async, CallModifier modifier) +{ + if (modifier == CallModifierNoSuspend) + return ErrorNone; + bool callee_is_async = false; + switch (callee->type_entry->data.fn.fn_type_id.cc) { + case CallingConventionUnspecified: + break; + case CallingConventionAsync: + callee_is_async = true; + break; + default: + return ErrorNone; + } + if (!callee_is_async) { + if (callee->anal_state == FnAnalStateReady) { + analyze_fn_body(g, callee); + if (callee->anal_state == FnAnalStateInvalid) { + return ErrorSemanticAnalyzeFail; + } + } + if (callee->anal_state == FnAnalStateComplete) { + analyze_fn_async(g, callee, true); + if (callee->anal_state == FnAnalStateInvalid) { + if (g->trace_err != nullptr) { + g->trace_err = add_error_note(g, g->trace_err, call_node, + buf_sprintf("while checking if '%s' is async", buf_ptr(&fn->symbol_name))); + } + return ErrorSemanticAnalyzeFail; + } + callee_is_async = fn_is_async(callee); + } else { + // If it's already been determined, use that value. Otherwise + // assume non-async, emit an error later if it turned out to be async. + if (callee->inferred_async_node == nullptr || + callee->inferred_async_node == inferred_async_checking) + { + callee->assumed_non_async = call_node; + callee_is_async = false; + } else { + callee_is_async = callee->inferred_async_node != inferred_async_none; + } + } + } + if (callee_is_async) { + bool bad_recursion = (fn->inferred_async_node == inferred_async_none); + fn->inferred_async_node = call_node; + fn->inferred_async_fn = callee; + if (must_not_be_async) { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("function with calling convention '%s' cannot be async", + calling_convention_name(fn->type_entry->data.fn.fn_type_id.cc))); + add_async_error_notes(g, msg, fn); + return ErrorSemanticAnalyzeFail; + } + if (bad_recursion) { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("recursive function cannot be async")); + add_async_error_notes(g, msg, fn); + return ErrorSemanticAnalyzeFail; + } + if (fn->assumed_non_async != nullptr) { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("unable to infer whether '%s' should be async", + buf_ptr(&fn->symbol_name))); + add_error_note(g, msg, fn->assumed_non_async, + buf_sprintf("assumed to be non-async here")); + add_async_error_notes(g, msg, fn); + fn->anal_state = FnAnalStateInvalid; + return ErrorSemanticAnalyzeFail; + } + return ErrorIsAsync; + } + return ErrorNone; +} + +// This function resolves functions being inferred async. +static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) { + if (fn->inferred_async_node == inferred_async_checking) { + // TODO call graph cycle detected, disallow the recursion + fn->inferred_async_node = inferred_async_none; + return; + } + if (fn->inferred_async_node == inferred_async_none) { + return; + } + if (fn->inferred_async_node != nullptr) { + if (resolve_frame) { + resolve_async_fn_frame(g, fn); + } + return; + } + fn->inferred_async_node = inferred_async_checking; + + bool must_not_be_async = false; + if (fn->type_entry->data.fn.fn_type_id.cc != CallingConventionUnspecified) { + must_not_be_async = true; + fn->inferred_async_node = inferred_async_none; + } + + for (size_t i = 0; i < fn->call_list.length; i += 1) { + IrInstGenCall *call = fn->call_list.at(i); + if (call->fn_entry == nullptr) { + // TODO function pointer call here, could be anything + continue; + } + switch (analyze_callee_async(g, fn, call->fn_entry, call->base.base.source_node, must_not_be_async, + call->modifier)) + { + case ErrorSemanticAnalyzeFail: + fn->anal_state = FnAnalStateInvalid; + return; + case ErrorNone: + continue; + case ErrorIsAsync: + if (resolve_frame) { + resolve_async_fn_frame(g, fn); + } + return; + default: + zig_unreachable(); + } + } + for (size_t i = 0; i < fn->await_list.length; i += 1) { + IrInstGenAwait *await = fn->await_list.at(i); + if (await->is_nosuspend) continue; + switch (analyze_callee_async(g, fn, await->target_fn, await->base.base.source_node, must_not_be_async, + CallModifierNone)) + { + case ErrorSemanticAnalyzeFail: + fn->anal_state = FnAnalStateInvalid; + return; + case ErrorNone: + continue; + case ErrorIsAsync: + if (resolve_frame) { + resolve_async_fn_frame(g, fn); + } + return; + default: + zig_unreachable(); + } + } + fn->inferred_async_node = inferred_async_none; +} + +static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) { + ZigType *fn_type = fn->type_entry; + assert(!fn_type->data.fn.is_generic); + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + + if (fn->analyzed_executable.begin_scope == nullptr) { + fn->analyzed_executable.begin_scope = &fn->def_scope->base; + } + if (fn->analyzed_executable.source_node == nullptr) { + fn->analyzed_executable.source_node = fn->body_node; + } + ZigType *block_return_type = ir_analyze(g, fn->ir_executable, + &fn->analyzed_executable, fn_type_id->return_type, return_type_node, nullptr); + fn->src_implicit_return_type = block_return_type; + + if (type_is_invalid(block_return_type) || fn->analyzed_executable.first_err_trace_msg != nullptr) { + assert(g->errors.length > 0); + fn->anal_state = FnAnalStateInvalid; + return; + } + + if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) { + ZigType *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type; + if (return_err_set_type->data.error_set.infer_fn != nullptr && + return_err_set_type->data.error_set.incomplete) + { + // The inferred error set type is null if the function doesn't + // return any error + ZigType *inferred_err_set_type = nullptr; + + if (fn->src_implicit_return_type->id == ZigTypeIdErrorSet) { + inferred_err_set_type = fn->src_implicit_return_type; + } else if (fn->src_implicit_return_type->id == ZigTypeIdErrorUnion) { + inferred_err_set_type = fn->src_implicit_return_type->data.error_union.err_set_type; + } + + if (inferred_err_set_type != nullptr) { + if (inferred_err_set_type->data.error_set.infer_fn != nullptr && + inferred_err_set_type->data.error_set.incomplete) + { + if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { + fn->anal_state = FnAnalStateInvalid; + return; + } + } + + return_err_set_type->data.error_set.incomplete = false; + if (type_is_global_error_set(inferred_err_set_type)) { + return_err_set_type->data.error_set.err_count = UINT32_MAX; + } else { + return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; + if (inferred_err_set_type->data.error_set.err_count > 0) { + return_err_set_type->data.error_set.errors = heap::c_allocator.allocate(inferred_err_set_type->data.error_set.err_count); + for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { + return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + } + } + } + } else { + return_err_set_type->data.error_set.incomplete = false; + return_err_set_type->data.error_set.err_count = 0; + } + } + } + + CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc; + if (cc != CallingConventionUnspecified && cc != CallingConventionAsync && + fn->inferred_async_node != nullptr && + fn->inferred_async_node != inferred_async_checking && + fn->inferred_async_node != inferred_async_none) + { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("function with calling convention '%s' cannot be async", + calling_convention_name(cc))); + add_async_error_notes(g, msg, fn); + fn->anal_state = FnAnalStateInvalid; + } + + if (g->verbose_ir) { + fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn->symbol_name)); + ir_print_gen(g, stderr, &fn->analyzed_executable, 4); + fprintf(stderr, "}\n"); + } + fn->anal_state = FnAnalStateComplete; +} + +static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) { + assert(fn_table_entry->anal_state != FnAnalStateProbing); + if (fn_table_entry->anal_state != FnAnalStateReady) + return; + + fn_table_entry->anal_state = FnAnalStateProbing; + update_progress_display(g); + + AstNode *return_type_node = (fn_table_entry->proto_node != nullptr) ? + fn_table_entry->proto_node->data.fn_proto.return_type : fn_table_entry->fndef_scope->base.source_node; + + assert(fn_table_entry->fndef_scope); + if (!fn_table_entry->child_scope) + fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base; + + if (define_local_param_variables(g, fn_table_entry) != ErrorNone) { + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } + + ZigType *fn_type = fn_table_entry->type_entry; + assert(!fn_type->data.fn.is_generic); + + if (!ir_gen_fn(g, fn_table_entry)) { + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } + + if (fn_table_entry->ir_executable->first_err_trace_msg != nullptr) { + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } + + if (g->verbose_ir) { + fprintf(stderr, "\n"); + ast_render(stderr, fn_table_entry->body_node, 4); + fprintf(stderr, "\nfn %s() { // (IR)\n", buf_ptr(&fn_table_entry->symbol_name)); + ir_print_src(g, stderr, fn_table_entry->ir_executable, 4); + fprintf(stderr, "}\n"); + } + + analyze_fn_ir(g, fn_table_entry, return_type_node); +} + +ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code, + SourceKind source_kind) +{ + if (g->verbose_tokenize) { + fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(resolved_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(resolved_path, tokenization.err_line, tokenization.err_column, + source_code, tokenization.line_offsets, tokenization.err); + + print_err_msg(err, g->err_color); + exit(1); + } + + if (g->verbose_tokenize) { + print_tokens(source_code, tokenization.tokens); + + fprintf(stderr, "\nAST:\n"); + fprintf(stderr, "------\n"); + } + + Buf *src_dirname = buf_alloc(); + Buf *src_basename = buf_alloc(); + os_path_split(resolved_path, src_dirname, src_basename); + + Buf noextname = BUF_INIT; + os_path_extname(resolved_path, &noextname, nullptr); + + Buf *pkg_root_src_dir = &package->root_src_dir; + Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1); + + Buf *namespace_name = buf_create_from_buf(&package->pkg_path); + if (source_kind == SourceKindNonRoot) { + assert(buf_starts_with_buf(resolved_path, &resolved_root_src_dir)); + if (buf_len(namespace_name) != 0) { + buf_append_char(namespace_name, NAMESPACE_SEP_CHAR); + } + // The namespace components are obtained from the relative path to the + // source directory + if (buf_len(&noextname) > buf_len(&resolved_root_src_dir)) { + // Skip the trailing separator + buf_append_mem(namespace_name, + buf_ptr(&noextname) + buf_len(&resolved_root_src_dir) + 1, + buf_len(&noextname) - buf_len(&resolved_root_src_dir) - 1); + } + buf_replace(namespace_name, ZIG_OS_SEP_CHAR, NAMESPACE_SEP_CHAR); + } + Buf *bare_name = buf_alloc(); + os_path_extname(src_basename, bare_name, nullptr); + + RootStruct *root_struct = heap::c_allocator.create(); + root_struct->package = package; + root_struct->source_code = source_code; + root_struct->line_offsets = tokenization.line_offsets; + root_struct->path = resolved_path; + root_struct->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); + ZigType *import_entry = get_root_container_type(g, buf_ptr(namespace_name), bare_name, root_struct); + if (source_kind == SourceKindRoot) { + assert(g->root_import == nullptr); + g->root_import = import_entry; + } + g->import_table.put(resolved_path, import_entry); + + AstNode *root_node = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color); + assert(root_node != nullptr); + assert(root_node->type == NodeTypeContainerDecl); + import_entry->data.structure.decl_node = root_node; + import_entry->data.structure.decls_scope->base.source_node = root_node; + if (g->verbose_ast) { + ast_print(stderr, root_node, 0); + } + + for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) { + AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i); + scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl); + } + + TldContainer *tld_container = heap::c_allocator.create(); + init_tld(&tld_container->base, TldIdContainer, namespace_name, VisibModPub, root_node, nullptr); + tld_container->type_entry = import_entry; + tld_container->decls_scope = import_entry->data.structure.decls_scope; + g->resolve_queue.append(&tld_container->base); + + return import_entry; +} + +void semantic_analyze(CodeGen *g) { + 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); + g->trace_err = nullptr; + AstNode *source_node = nullptr; + resolve_top_level_decl(g, tld, source_node, false); + } + + for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) { + ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index); + g->trace_err = nullptr; + analyze_fn_body(g, fn_entry); + } + } + + if (g->errors.length != 0) { + return; + } + + // second pass over functions for detecting async + for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) { + ZigFn *fn = g->fn_defs.at(g->fn_defs_index); + g->trace_err = nullptr; + analyze_fn_async(g, fn, true); + if (fn->anal_state == FnAnalStateInvalid) + continue; + if (fn_is_async(fn) && fn->non_async_node != nullptr) { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name))); + add_error_note(g, msg, fn->non_async_node, + buf_sprintf("required to be non-async here")); + add_async_error_notes(g, msg, fn); + } + } +} + +ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { + assert(size_in_bits <= 65535); + TypeId type_id = {}; + type_id.id = ZigTypeIdInt; + 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; + } + + ZigType *new_entry = make_int_type(g, is_signed, size_in_bits); + g->type_table.put(type_id, new_entry); + return new_entry; +} + +Error is_valid_vector_elem_type(CodeGen *g, ZigType *elem_type, bool *result) { + if (elem_type->id == ZigTypeIdInt || + elem_type->id == ZigTypeIdFloat || + elem_type->id == ZigTypeIdBool) + { + *result = true; + return ErrorNone; + } + + Error err; + ZigType *ptr_type; + if ((err = get_codegen_ptr_type(g, elem_type, &ptr_type))) return err; + if (ptr_type != nullptr) { + *result = true; + return ErrorNone; + } + + *result = false; + return ErrorNone; +} + +ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) { + Error err; + + bool valid_vector_elem; + if ((err = is_valid_vector_elem_type(g, elem_type, &valid_vector_elem))) { + codegen_report_errors_and_exit(g); + } + assert(valid_vector_elem); + + TypeId type_id = {}; + type_id.id = ZigTypeIdVector; + type_id.data.vector.len = len; + type_id.data.vector.elem_type = elem_type; + + { + auto entry = g->type_table.maybe_get(type_id); + if (entry) + return entry->value; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdVector); + if ((len != 0) && type_has_bits(g, elem_type)) { + // Vectors can only be ints, floats, bools, or pointers. ints (inc. bools) and floats have trivially resolvable + // llvm type refs. pointers we will use usize instead. + LLVMTypeRef example_vector_llvm_type; + if (elem_type->id == ZigTypeIdPointer) { + example_vector_llvm_type = LLVMVectorType(g->builtin_types.entry_usize->llvm_type, len); + } else { + example_vector_llvm_type = LLVMVectorType(elem_type->llvm_type, len); + } + assert(example_vector_llvm_type != nullptr); + entry->size_in_bits = elem_type->size_in_bits * len; + entry->abi_size = LLVMABISizeOfType(g->target_data_ref, example_vector_llvm_type); + entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, example_vector_llvm_type); + } + entry->data.vector.len = len; + entry->data.vector.elem_type = elem_type; + entry->data.vector.padding = 0; + + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name)); + + g->type_table.put(type_id, entry); + return entry; +} + +ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) { + return &g->builtin_types.entry_c_int[c_int_type]; +} + +ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type) { + return *get_c_int_type_ptr(g, c_int_type); +} + +bool handle_is_ptr(CodeGen *g, ZigType *type_entry) { + switch (type_entry->id) { + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + zig_unreachable(); + case ZigTypeIdUnreachable: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdErrorSet: + case ZigTypeIdFn: + case ZigTypeIdEnum: + case ZigTypeIdVector: + case ZigTypeIdAnyFrame: + return false; + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdFnFrame: + return type_has_bits(g, type_entry); + case ZigTypeIdErrorUnion: + return type_has_bits(g, type_entry->data.error_union.payload_type); + case ZigTypeIdOptional: + return type_has_bits(g, type_entry->data.maybe.child_type) && + !type_is_nonnull_ptr(g, type_entry->data.maybe.child_type) && + type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet; + case ZigTypeIdUnion: + return type_has_bits(g, type_entry) && type_entry->data.unionation.gen_field_count != 0; + + } + zig_unreachable(); +} + +static uint32_t hash_ptr(void *ptr) { + return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX); +} + +static uint32_t hash_size(size_t x) { + return (uint32_t)(x % UINT32_MAX); +} + +uint32_t fn_table_entry_hash(ZigFn* value) { + return ptr_hash(value); +} + +bool fn_table_entry_eql(ZigFn *a, ZigFn *b) { + return ptr_eq(a, b); +} + +uint32_t fn_type_id_hash(FnTypeId *id) { + uint32_t result = 0; + result += ((uint32_t)(id->cc)) * (uint32_t)3349388391; + result += id->is_var_args ? (uint32_t)1931444534 : 0; + result += hash_ptr(id->return_type); + result += id->alignment * 0xd3b3f3e2; + for (size_t i = 0; i < id->param_count; i += 1) { + FnTypeParamInfo *info = &id->param_info[i]; + result += info->is_noalias ? (uint32_t)892356923 : 0; + result += hash_ptr(info->type); + } + return result; +} + +bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { + if (a->cc != b->cc || + a->return_type != b->return_type || + a->is_var_args != b->is_var_args || + a->param_count != b->param_count || + a->alignment != b->alignment) + { + 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_error_set(ZigValue *const_val) { + assert(const_val->data.x_err_set != nullptr); + return const_val->data.x_err_set->value ^ 2630160122; +} + +static uint32_t hash_const_val_ptr(ZigValue *const_val) { + uint32_t hash_val = 0; + switch (const_val->data.x_ptr.mut) { + case ConstPtrMutRuntimeVar: + hash_val += (uint32_t)3500721036; + break; + case ConstPtrMutComptimeConst: + hash_val += (uint32_t)4214318515; + break; + case ConstPtrMutInfer: + case ConstPtrMutComptimeVar: + hash_val += (uint32_t)1103195694; + break; + } + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + hash_val += (uint32_t)2478261866; + hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee); + return hash_val; + case ConstPtrSpecialBaseArray: + hash_val += (uint32_t)1764906839; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); + return hash_val; + case ConstPtrSpecialSubArray: + hash_val += (uint32_t)2643358777; + 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); + return hash_val; + case ConstPtrSpecialBaseStruct: + hash_val += (uint32_t)3518317043; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); + return hash_val; + case ConstPtrSpecialBaseErrorUnionCode: + hash_val += (uint32_t)2994743799; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val); + return hash_val; + case ConstPtrSpecialBaseErrorUnionPayload: + hash_val += (uint32_t)3456080131; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val); + return hash_val; + case ConstPtrSpecialBaseOptionalPayload: + hash_val += (uint32_t)3163140517; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val); + return hash_val; + case ConstPtrSpecialHardCodedAddr: + hash_val += (uint32_t)4048518294; + hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); + return hash_val; + case ConstPtrSpecialDiscard: + hash_val += 2010123162; + return hash_val; + case ConstPtrSpecialFunction: + hash_val += (uint32_t)2590901619; + hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); + return hash_val; + case ConstPtrSpecialNull: + hash_val += (uint32_t)1486246455; + return hash_val; + } + zig_unreachable(); +} + +static uint32_t hash_const_val(ZigValue *const_val) { + assert(const_val->special == ConstValSpecialStatic); + switch (const_val->type->id) { + case ZigTypeIdOpaque: + zig_unreachable(); + case ZigTypeIdBool: + return const_val->data.x_bool ? (uint32_t)127863866 : (uint32_t)215080464; + case ZigTypeIdMetaType: + return hash_ptr(const_val->data.x_type); + case ZigTypeIdVoid: + return (uint32_t)4149439618; + case ZigTypeIdInt: + case ZigTypeIdComptimeInt: + { + uint32_t result = 1331471175; + for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) { + uint64_t digit = bigint_ptr(&const_val->data.x_bigint)[i]; + result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result); + } + return result; + } + case ZigTypeIdEnumLiteral: + return buf_hash(const_val->data.x_enum_literal) * (uint32_t)2691276464; + case ZigTypeIdEnum: + { + uint32_t result = 31643936; + for (size_t i = 0; i < const_val->data.x_enum_tag.digit_count; i += 1) { + uint64_t digit = bigint_ptr(&const_val->data.x_enum_tag)[i]; + result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result); + } + return result; + } + case ZigTypeIdFloat: + switch (const_val->type->data.floating.bit_count) { + case 16: + { + uint16_t result; + static_assert(sizeof(result) == sizeof(const_val->data.x_f16), ""); + memcpy(&result, &const_val->data.x_f16, sizeof(result)); + return result * 65537u; + } + case 32: + { + uint32_t result; + memcpy(&result, &const_val->data.x_f32, 4); + return result ^ 4084870010; + } + case 64: + { + uint32_t ints[2]; + memcpy(&ints[0], &const_val->data.x_f64, 8); + return ints[0] ^ ints[1] ^ 0x22ed43c6; + } + case 128: + { + uint32_t ints[4]; + memcpy(&ints[0], &const_val->data.x_f128, 16); + return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xb5ffef27; + } + default: + zig_unreachable(); + } + case ZigTypeIdComptimeFloat: + { + float128_t f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); + uint32_t ints[4]; + memcpy(&ints[0], &f128, 16); + return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xed8b3dfb; + } + case ZigTypeIdFn: + assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); + assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); + return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); + case ZigTypeIdPointer: + return hash_const_val_ptr(const_val); + case ZigTypeIdUndefined: + return 162837799; + case ZigTypeIdNull: + return 844854567; + case ZigTypeIdArray: + // TODO better hashing algorithm + return 1166190605; + case ZigTypeIdStruct: + // TODO better hashing algorithm + return 1532530855; + case ZigTypeIdUnion: + // TODO better hashing algorithm + return 2709806591; + case ZigTypeIdOptional: + if (get_src_ptr_type(const_val->type) != nullptr) { + return hash_const_val_ptr(const_val) * (uint32_t)1992916303; + } else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) { + return hash_const_val_error_set(const_val) * (uint32_t)3147031929; + } else { + if (const_val->data.x_optional) { + return hash_const_val(const_val->data.x_optional) * (uint32_t)1992916303; + } else { + return 4016830364; + } + } + case ZigTypeIdErrorUnion: + // TODO better hashing algorithm + return 3415065496; + case ZigTypeIdErrorSet: + return hash_const_val_error_set(const_val); + case ZigTypeIdVector: + // TODO better hashing algorithm + return 3647867726; + case ZigTypeIdFnFrame: + // TODO better hashing algorithm + return 675741936; + case ZigTypeIdAnyFrame: + // TODO better hashing algorithm + return 3747294894; + case ZigTypeIdBoundFn: { + assert(const_val->data.x_bound_fn.fn != nullptr); + return 3677364617 ^ hash_ptr(const_val->data.x_bound_fn.fn); + } + case ZigTypeIdInvalid: + case ZigTypeIdUnreachable: + 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) { + ZigValue *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) { + ZigValue *a_val = &a->params[i]; + ZigValue *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->codegen, a_val, b_val)) { + return false; + } + } else { + assert(a_val->special == ConstValSpecialRuntime && b_val->special == ConstValSpecialRuntime); + } + } + return true; +} + +static bool can_mutate_comptime_var_state(ZigValue *value) { + assert(value != nullptr); + if (value->special == ConstValSpecialUndef) + return false; + switch (value->type->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdInt: + case ZigTypeIdVector: + case ZigTypeIdFloat: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + case ZigTypeIdFn: + case ZigTypeIdOpaque: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + return false; + + case ZigTypeIdPointer: + return value->data.x_ptr.mut == ConstPtrMutComptimeVar; + + case ZigTypeIdArray: + if (value->special == ConstValSpecialUndef) + return false; + if (value->type->data.array.len == 0) + return false; + switch (value->data.x_array.special) { + case ConstArraySpecialUndef: + case ConstArraySpecialBuf: + return false; + case ConstArraySpecialNone: + for (uint32_t i = 0; i < value->type->data.array.len; i += 1) { + if (can_mutate_comptime_var_state(&value->data.x_array.data.s_none.elements[i])) + return true; + } + return false; + } + zig_unreachable(); + case ZigTypeIdStruct: + for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) { + if (can_mutate_comptime_var_state(value->data.x_struct.fields[i])) + return true; + } + return false; + + case ZigTypeIdOptional: + if (get_src_ptr_type(value->type) != nullptr) + return value->data.x_ptr.mut == ConstPtrMutComptimeVar; + if (value->data.x_optional == nullptr) + return false; + return can_mutate_comptime_var_state(value->data.x_optional); + + case ZigTypeIdErrorUnion: + if (value->data.x_err_union.error_set->data.x_err_set != nullptr) + return false; + assert(value->data.x_err_union.payload != nullptr); + return can_mutate_comptime_var_state(value->data.x_err_union.payload); + + case ZigTypeIdUnion: + return can_mutate_comptime_var_state(value->data.x_union.payload); + } + zig_unreachable(); +} + +static bool return_type_is_cacheable(ZigType *return_type) { + switch (return_type->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + case ZigTypeIdFn: + case ZigTypeIdOpaque: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdPointer: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + return true; + + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdUnion: + return false; + + case ZigTypeIdOptional: + return return_type_is_cacheable(return_type->data.maybe.child_type); + + case ZigTypeIdErrorUnion: + return return_type_is_cacheable(return_type->data.error_union.payload_type); + } + zig_unreachable(); +} + +bool fn_eval_cacheable(Scope *scope, ZigType *return_type) { + if (!return_type_is_cacheable(return_type)) + return false; + while (scope) { + if (scope->id == ScopeIdVarDecl) { + ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; + if (type_is_invalid(var_scope->var->var_type)) + return false; + if (var_scope->var->const_value->special == ConstValSpecialUndef) + return false; + if (can_mutate_comptime_var_state(var_scope->var->const_value)) + return false; + } else if (scope->id == ScopeIdFnDef) { + return true; + } else { + zig_unreachable(); + } + + scope = scope->parent; + } + zig_unreachable(); +} + +uint32_t fn_eval_hash(Scope* scope) { + uint32_t result = 0; + while (scope) { + if (scope->id == ScopeIdVarDecl) { + ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; + result += hash_const_val(var_scope->var->const_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) { + assert(a->codegen != nullptr); + assert(b->codegen != nullptr); + 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->var_type != b_var_scope->var->var_type) + return false; + if (a_var_scope->var->var_type == a_var_scope->var->const_value->type && + b_var_scope->var->var_type == b_var_scope->var->const_value->type) + { + if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value)) + return false; + } else { + zig_panic("TODO comptime ptr reinterpret for fn_eval_eql"); + } + } 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; +} + +// Deprecated. Use type_has_bits2. +bool type_has_bits(CodeGen *g, ZigType *type_entry) { + Error err; + bool result; + if ((err = type_has_bits2(g, type_entry, &result))) { + codegen_report_errors_and_exit(g); + } + return result; +} + +// Whether the type has bits at runtime. +Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result) { + Error err; + + if (type_is_invalid(type_entry)) + return ErrorSemanticAnalyzeFail; + + if (type_entry->id == ZigTypeIdStruct && + type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) + { + *result = true; + return ErrorNone; + } + + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return err; + + *result = type_entry->abi_size != 0; + return ErrorNone; +} + +// Whether you can infer the value based solely on the type. +OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { + assert(type_entry != nullptr); + + if (type_entry->one_possible_value != OnePossibleValueInvalid) + return type_entry->one_possible_value; + + if (type_entry->id == ZigTypeIdStruct && + type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) + { + return OnePossibleValueNo; + } + + Error err; + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return OnePossibleValueInvalid; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdOpaque: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdMetaType: + case ZigTypeIdBoundFn: + case ZigTypeIdOptional: + case ZigTypeIdFn: + case ZigTypeIdBool: + case ZigTypeIdFloat: + case ZigTypeIdErrorUnion: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + return OnePossibleValueNo; + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + return OnePossibleValueYes; + case ZigTypeIdArray: + if (type_entry->data.array.len == 0) + return OnePossibleValueYes; + return type_has_one_possible_value(g, type_entry->data.array.child_type); + case ZigTypeIdStruct: + // If the recursive function call asks, then we are not one possible value. + type_entry->one_possible_value = OnePossibleValueNo; + for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { + TypeStructField *field = type_entry->data.structure.fields[i]; + if (field->is_comptime) { + // If this field is comptime then the field can only be one possible value + continue; + } + OnePossibleValue opv = (field->type_entry != nullptr) ? + type_has_one_possible_value(g, field->type_entry) : + type_val_resolve_has_one_possible_value(g, field->type_val); + switch (opv) { + case OnePossibleValueInvalid: + type_entry->one_possible_value = OnePossibleValueInvalid; + return OnePossibleValueInvalid; + case OnePossibleValueNo: + return OnePossibleValueNo; + case OnePossibleValueYes: + continue; + } + } + type_entry->one_possible_value = OnePossibleValueYes; + return OnePossibleValueYes; + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdInt: + case ZigTypeIdVector: + return type_has_bits(g, type_entry) ? OnePossibleValueNo : OnePossibleValueYes; + case ZigTypeIdPointer: { + ZigType *elem_type = type_entry->data.pointer.child_type; + // If the recursive function call asks, then we are not one possible value. + type_entry->one_possible_value = OnePossibleValueNo; + // Now update it to be the value of the recursive call. + type_entry->one_possible_value = type_has_one_possible_value(g, elem_type); + return type_entry->one_possible_value; + } + case ZigTypeIdUnion: + if (type_entry->data.unionation.src_field_count > 1) + return OnePossibleValueNo; + TypeUnionField *only_field = &type_entry->data.unionation.fields[0]; + if (only_field->type_entry != nullptr) { + return type_has_one_possible_value(g, only_field->type_entry); + } + return type_val_resolve_has_one_possible_value(g, only_field->type_val); + } + zig_unreachable(); +} + +ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) { + auto entry = g->one_possible_values.maybe_get(type_entry); + if (entry != nullptr) { + return entry->value; + } + ZigValue *result = g->pass1_arena->create(); + result->type = type_entry; + result->special = ConstValSpecialStatic; + + if (result->type->id == ZigTypeIdStruct) { + // The fields array cannot be left unpopulated + const ZigType *struct_type = result->type; + const size_t field_count = struct_type->data.structure.src_field_count; + result->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count); + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + if (field->is_comptime) { + copy_const_val(g, result->data.x_struct.fields[i], field->init_val); + continue; + } + ZigType *field_type = resolve_struct_field_type(g, field); + assert(field_type != nullptr); + result->data.x_struct.fields[i] = get_the_one_possible_value(g, field_type); + } + } else if (result->type->id == ZigTypeIdArray) { + // The elements array cannot be left unpopulated + ZigType *array_type = result->type; + ZigType *elem_type = array_type->data.array.child_type; + const size_t elem_count = array_type->data.array.len; + + result->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count); + for (size_t i = 0; i < elem_count; i += 1) { + ZigValue *elem_val = &result->data.x_array.data.s_none.elements[i]; + copy_const_val(g, elem_val, get_the_one_possible_value(g, elem_type)); + } + } else if (result->type->id == ZigTypeIdPointer) { + result->data.x_ptr.special = ConstPtrSpecialRef; + result->data.x_ptr.data.ref.pointee = get_the_one_possible_value(g, result->type->data.pointer.child_type); + } + g->one_possible_values.put(type_entry, result); + return result; +} + +ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) { + Error err; + if (ty == g->builtin_types.entry_anytype) { + return ReqCompTimeYes; + } + switch (ty->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdMetaType: + case ZigTypeIdBoundFn: + return ReqCompTimeYes; + case ZigTypeIdArray: + return type_requires_comptime(g, ty->data.array.child_type); + case ZigTypeIdStruct: + if (ty->data.structure.resolve_loop_flag_zero_bits) { + // Does a struct which contains a pointer field to itself require comptime? No. + return ReqCompTimeNo; + } + if ((err = type_resolve(g, ty, ResolveStatusZeroBitsKnown))) + return ReqCompTimeInvalid; + return ty->data.structure.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo; + case ZigTypeIdUnion: + if (ty->data.unionation.resolve_loop_flag_zero_bits) { + // Does a union which contains a pointer field to itself require comptime? No. + return ReqCompTimeNo; + } + if ((err = type_resolve(g, ty, ResolveStatusZeroBitsKnown))) + return ReqCompTimeInvalid; + return ty->data.unionation.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo; + case ZigTypeIdOptional: + return type_requires_comptime(g, ty->data.maybe.child_type); + case ZigTypeIdErrorUnion: + return type_requires_comptime(g, ty->data.error_union.payload_type); + case ZigTypeIdPointer: + if (ty->data.pointer.child_type->id == ZigTypeIdOpaque) { + return ReqCompTimeNo; + } else { + return type_requires_comptime(g, ty->data.pointer.child_type); + } + case ZigTypeIdFn: + return ty->data.fn.is_generic ? ReqCompTimeYes : ReqCompTimeNo; + case ZigTypeIdOpaque: + case ZigTypeIdEnum: + case ZigTypeIdErrorSet: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdVector: + case ZigTypeIdFloat: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + return ReqCompTimeNo; + } + zig_unreachable(); +} + +void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) { + auto entry = g->string_literals_table.maybe_get(str); + if (entry != nullptr) { + memcpy(const_val, entry->value, sizeof(ZigValue)); + return; + } + + // first we build the underlying array + ZigValue *array_val = g->pass1_arena->create(); + array_val->special = ConstValSpecialStatic; + array_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), g->intern.for_zero_byte()); + array_val->data.x_array.special = ConstArraySpecialBuf; + array_val->data.x_array.data.s_buf = str; + + // then make the pointer point to it + const_val->special = ConstValSpecialStatic; + const_val->type = get_pointer_to_type_extra2(g, array_val->type, true, false, + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr); + const_val->data.x_ptr.special = ConstPtrSpecialRef; + const_val->data.x_ptr.data.ref.pointee = array_val; + + g->string_literals_table.put(str, const_val); +} + +ZigValue *create_const_str_lit(CodeGen *g, Buf *str) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_str_lit(g, const_val, str); + return const_val; +} + +void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + bigint_init_bigint(&const_val->data.x_bigint, bigint); +} + +ZigValue *create_const_bigint(CodeGen *g, ZigType *type, const BigInt *bigint) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_bigint(const_val, type, bigint); + return const_val; +} + + +void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + bigint_init_unsigned(&const_val->data.x_bigint, x); + const_val->data.x_bigint.is_negative = negative; +} + +ZigValue *create_const_unsigned_negative(CodeGen *g, ZigType *type, uint64_t x, bool negative) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_unsigned_negative(const_val, type, x, negative); + return const_val; +} + +void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x) { + return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false); +} + +ZigValue *create_const_usize(CodeGen *g, uint64_t x) { + return create_const_unsigned_negative(g, g->builtin_types.entry_usize, x, false); +} + +void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + bigint_init_signed(&const_val->data.x_bigint, x); +} + +ZigValue *create_const_signed(CodeGen *g, ZigType *type, int64_t x) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_signed(const_val, type, x); + return const_val; +} + +void init_const_null(ZigValue *const_val, ZigType *type) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + const_val->data.x_optional = nullptr; +} + +ZigValue *create_const_null(CodeGen *g, ZigType *type) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_null(const_val, type); + return const_val; +} + +void init_const_fn(ZigValue *const_val, ZigFn *fn) { + const_val->special = ConstValSpecialStatic; + const_val->type = fn->type_entry; + const_val->data.x_ptr.special = ConstPtrSpecialFunction; + const_val->data.x_ptr.data.fn.fn_entry = fn; +} + +ZigValue *create_const_fn(CodeGen *g, ZigFn *fn) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_fn(const_val, fn); + return const_val; +} + +void init_const_float(ZigValue *const_val, ZigType *type, double value) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + if (type->id == ZigTypeIdComptimeFloat) { + bigfloat_init_64(&const_val->data.x_bigfloat, value); + } else if (type->id == ZigTypeIdFloat) { + switch (type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = zig_double_to_f16(value); + break; + case 32: + const_val->data.x_f32 = value; + break; + case 64: + const_val->data.x_f64 = value; + break; + case 128: + // if we need this, we should add a function that accepts a float128_t param + zig_unreachable(); + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +ZigValue *create_const_float(CodeGen *g, ZigType *type, double value) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_float(const_val, type, value); + return const_val; +} + +void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + bigint_init_bigint(&const_val->data.x_enum_tag, tag); +} + +ZigValue *create_const_enum(CodeGen *g, ZigType *type, const BigInt *tag) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_enum(const_val, type, tag); + return const_val; +} + + +void init_const_bool(CodeGen *g, ZigValue *const_val, bool value) { + const_val->special = ConstValSpecialStatic; + const_val->type = g->builtin_types.entry_bool; + const_val->data.x_bool = value; +} + +ZigValue *create_const_bool(CodeGen *g, bool value) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_bool(g, const_val, value); + return const_val; +} + +void init_const_runtime(ZigValue *const_val, ZigType *type) { + const_val->special = ConstValSpecialRuntime; + const_val->type = type; +} + +ZigValue *create_const_runtime(CodeGen *g, ZigType *type) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_runtime(const_val, type); + return const_val; +} + +void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value) { + const_val->special = ConstValSpecialStatic; + const_val->type = g->builtin_types.entry_type; + const_val->data.x_type = type_value; +} + +ZigValue *create_const_type(CodeGen *g, ZigType *type_value) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_type(g, const_val, type_value); + return const_val; +} + +void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val, + size_t start, size_t len, bool is_const) +{ + assert(array_val->type->id == ZigTypeIdArray); + + ZigType *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type, + is_const, false, PtrLenUnknown, 0, 0, 0, false); + + const_val->special = ConstValSpecialStatic; + const_val->type = get_slice_type(g, ptr_type); + const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, 2); + + init_const_ptr_array(g, const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const, + PtrLenUnknown); + init_const_usize(g, const_val->data.x_struct.fields[slice_len_index], len); +} + +ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_slice(g, const_val, array_val, start, len, is_const); + return const_val; +} + +void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val, + size_t elem_index, bool is_const, PtrLen ptr_len) +{ + assert(array_val->type->id == ZigTypeIdArray); + ZigType *child_type = array_val->type->data.array.child_type; + + const_val->special = ConstValSpecialStatic; + const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false, + ptr_len, 0, 0, 0, false); + 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; +} + +ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index, bool is_const, + PtrLen ptr_len) +{ + ZigValue *const_val = g->pass1_arena->create(); + init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len); + return const_val; +} + +void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *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; +} + +ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const) { + ZigValue *const_val = g->pass1_arena->create(); + init_const_ptr_ref(g, const_val, pointee_val, is_const); + return const_val; +} + +void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *pointee_type, + size_t addr, bool is_const) +{ + const_val->special = ConstValSpecialStatic; + const_val->type = get_pointer_to_type(g, pointee_type, is_const); + const_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + const_val->data.x_ptr.data.hard_coded_addr.addr = addr; +} + +ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type, + size_t addr, bool is_const) +{ + ZigValue *const_val = g->pass1_arena->create(); + init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const); + return const_val; +} + +ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count) { + return realloc_const_vals_ptrs(g, nullptr, 0, count); +} + +ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count) { + assert(new_count >= old_count); + + size_t new_item_count = new_count - old_count; + ZigValue **result = heap::c_allocator.reallocate(ptr, old_count, new_count); + ZigValue *vals = g->pass1_arena->allocate(new_item_count); + for (size_t i = old_count; i < new_count; i += 1) { + result[i] = &vals[i - old_count]; + } + return result; +} + +TypeStructField **alloc_type_struct_fields(size_t count) { + return realloc_type_struct_fields(nullptr, 0, count); +} + +TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count) { + assert(new_count >= old_count); + + size_t new_item_count = new_count - old_count; + TypeStructField **result = heap::c_allocator.reallocate(ptr, old_count, new_count); + TypeStructField *vals = heap::c_allocator.allocate(new_item_count); + for (size_t i = old_count; i < new_count; i += 1) { + result[i] = &vals[i - old_count]; + } + return result; +} + +static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) { + if (orig_fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) + return orig_fn_type; + + ZigType *fn_type = heap::c_allocator.allocate_nonzero(1); + *fn_type = *orig_fn_type; + fn_type->data.fn.fn_type_id.cc = CallingConventionAsync; + fn_type->llvm_type = nullptr; + fn_type->llvm_di_type = nullptr; + + return fn_type; +} + +// Traverse up to the very top ExprScope, which has children. +// We have just arrived at the top from a child. That child, +// and its next siblings, do not need to be marked. But the previous +// siblings do. +// x + (await y) +// vs +// (await y) + x +static void mark_suspension_point(Scope *scope) { + ScopeExpr *child_expr_scope = (scope->id == ScopeIdExpr) ? reinterpret_cast(scope) : nullptr; + bool looking_for_exprs = true; + for (;;) { + scope = scope->parent; + switch (scope->id) { + case ScopeIdDeferExpr: + case ScopeIdDecls: + case ScopeIdFnDef: + case ScopeIdCompTime: + case ScopeIdNoSuspend: + case ScopeIdCImport: + case ScopeIdSuspend: + case ScopeIdTypeOf: + return; + case ScopeIdVarDecl: + case ScopeIdDefer: + case ScopeIdBlock: + looking_for_exprs = false; + continue; + case ScopeIdRuntime: + continue; + case ScopeIdLoop: { + ScopeLoop *loop_scope = reinterpret_cast(scope); + if (loop_scope->spill_scope != nullptr) { + loop_scope->spill_scope->need_spill = MemoizedBoolTrue; + } + looking_for_exprs = false; + continue; + } + case ScopeIdExpr: { + ScopeExpr *parent_expr_scope = reinterpret_cast(scope); + if (!looking_for_exprs) { + if (parent_expr_scope->spill_harder) { + parent_expr_scope->need_spill = MemoizedBoolTrue; + } + // Now we're only looking for a block, to see if it's in a loop (see the case ScopeIdBlock) + continue; + } + if (child_expr_scope != nullptr) { + for (size_t i = 0; parent_expr_scope->children_ptr[i] != child_expr_scope; i += 1) { + assert(i < parent_expr_scope->children_len); + parent_expr_scope->children_ptr[i]->need_spill = MemoizedBoolTrue; + } + } + parent_expr_scope->need_spill = MemoizedBoolTrue; + child_expr_scope = parent_expr_scope; + continue; + } + } + } +} + +static bool scope_needs_spill(Scope *scope) { + ScopeExpr *scope_expr = find_expr_scope(scope); + if (scope_expr == nullptr) return false; + + switch (scope_expr->need_spill) { + case MemoizedBoolUnknown: + if (scope_needs_spill(scope_expr->base.parent)) { + scope_expr->need_spill = MemoizedBoolTrue; + return true; + } else { + scope_expr->need_spill = MemoizedBoolFalse; + return false; + } + case MemoizedBoolFalse: + return false; + case MemoizedBoolTrue: + return true; + } + zig_unreachable(); +} + +static ZigType *resolve_type_isf(ZigType *ty) { + if (ty->id != ZigTypeIdPointer) return ty; + InferredStructField *isf = ty->data.pointer.inferred_struct_field; + if (isf == nullptr) return ty; + TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); + assert(field != nullptr); + return field->type_entry; +} + +static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { + Error err; + + if (frame_type->data.frame.locals_struct != nullptr) + return ErrorNone; + + ZigFn *fn = frame_type->data.frame.fn; + assert(!fn->type_entry->data.fn.is_generic); + + if (frame_type->data.frame.resolve_loop_type != nullptr) { + if (!frame_type->data.frame.reported_loop_err) { + add_node_error(g, fn->proto_node, + buf_sprintf("'%s' depends on itself", buf_ptr(&frame_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + switch (fn->anal_state) { + case FnAnalStateInvalid: + return ErrorSemanticAnalyzeFail; + case FnAnalStateComplete: + break; + case FnAnalStateReady: + analyze_fn_body(g, fn); + if (fn->anal_state == FnAnalStateInvalid) + return ErrorSemanticAnalyzeFail; + break; + case FnAnalStateProbing: { + add_node_error(g, fn->proto_node, + buf_sprintf("cannot resolve '%s': function not fully analyzed yet", + buf_ptr(&frame_type->name))); + return ErrorSemanticAnalyzeFail; + } + } + analyze_fn_async(g, fn, false); + if (fn->anal_state == FnAnalStateInvalid) + return ErrorSemanticAnalyzeFail; + + if (!fn_is_async(fn)) { + ZigType *fn_type = fn->type_entry; + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false); + + // label (grep this): [fn_frame_struct_layout] + ZigList fields = {}; + + fields.append({"@fn_ptr", g->builtin_types.entry_usize, 0}); + fields.append({"@resume_index", g->builtin_types.entry_usize, 0}); + fields.append({"@awaiter", g->builtin_types.entry_usize, 0}); + + fields.append({"@result_ptr_callee", ptr_return_type, 0}); + fields.append({"@result_ptr_awaiter", ptr_return_type, 0}); + fields.append({"@result", fn_type_id->return_type, 0}); + + if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) { + ZigType *ptr_to_stack_trace_type = get_pointer_to_type(g, get_stack_trace_type(g), false); + fields.append({"@ptr_stack_trace_callee", ptr_to_stack_trace_type, 0}); + fields.append({"@ptr_stack_trace_awaiter", ptr_to_stack_trace_type, 0}); + + fields.append({"@stack_trace", get_stack_trace_type(g), 0}); + fields.append({"@instruction_addresses", + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0}); + } + + frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name), + fields.items, fields.length, target_fn_align(g->zig_target)); + frame_type->abi_size = frame_type->data.frame.locals_struct->abi_size; + frame_type->abi_align = frame_type->data.frame.locals_struct->abi_align; + frame_type->size_in_bits = frame_type->data.frame.locals_struct->size_in_bits; + + return ErrorNone; + } + + ZigType *fn_type = get_async_fn_type(g, fn->type_entry); + + if (fn->analyzed_executable.need_err_code_spill) { + IrInstGenAlloca *alloca_gen = heap::c_allocator.create(); + alloca_gen->base.id = IrInstGenIdAlloca; + alloca_gen->base.base.source_node = fn->proto_node; + alloca_gen->base.base.scope = fn->child_scope; + alloca_gen->base.value = g->pass1_arena->create(); + alloca_gen->base.value->type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false); + alloca_gen->base.base.ref_count = 1; + alloca_gen->name_hint = ""; + fn->alloca_gen_list.append(alloca_gen); + fn->err_code_spill = &alloca_gen->base; + } + + ZigType *largest_call_frame_type = nullptr; + // Later we'll change this to be largest_call_frame_type instead of void. + IrInstGen *all_calls_alloca = ir_create_alloca(g, &fn->fndef_scope->base, fn->body_node, + fn, g->builtin_types.entry_void, "@async_call_frame"); + + for (size_t i = 0; i < fn->call_list.length; i += 1) { + IrInstGenCall *call = fn->call_list.at(i); + if (call->new_stack != nullptr) { + // don't need to allocate a frame for this + continue; + } + ZigFn *callee = call->fn_entry; + if (callee == nullptr) { + if (call->fn_ref->value->type->data.fn.fn_type_id.cc != CallingConventionAsync) { + continue; + } + add_node_error(g, call->base.base.source_node, + buf_sprintf("function is not comptime-known; @asyncCall required")); + return ErrorSemanticAnalyzeFail; + } + if (callee->body_node == nullptr) { + continue; + } + if (callee->anal_state == FnAnalStateProbing) { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("unable to determine async function frame of '%s'", buf_ptr(&fn->symbol_name))); + g->trace_err = add_error_note(g, msg, call->base.base.source_node, + buf_sprintf("analysis of function '%s' depends on the frame", buf_ptr(&callee->symbol_name))); + return ErrorSemanticAnalyzeFail; + } + + ZigType *callee_frame_type = get_fn_frame_type(g, callee); + frame_type->data.frame.resolve_loop_type = callee_frame_type; + frame_type->data.frame.resolve_loop_src_node = call->base.base.source_node; + + analyze_fn_body(g, callee); + if (callee->anal_state == FnAnalStateInvalid) { + frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid; + return ErrorSemanticAnalyzeFail; + } + analyze_fn_async(g, callee, true); + if (callee->inferred_async_node == inferred_async_checking) { + assert(g->errors.length != 0); + frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid; + return ErrorSemanticAnalyzeFail; + } + if (!fn_is_async(callee)) + continue; + + mark_suspension_point(call->base.base.scope); + + if ((err = type_resolve(g, callee_frame_type, ResolveStatusSizeKnown))) { + return err; + } + if (largest_call_frame_type == nullptr || + callee_frame_type->abi_size > largest_call_frame_type->abi_size) + { + largest_call_frame_type = callee_frame_type; + } + + call->frame_result_loc = all_calls_alloca; + } + if (largest_call_frame_type != nullptr) { + all_calls_alloca->value->type = get_pointer_to_type(g, largest_call_frame_type, false); + } + + // Since this frame is async, an await might represent a suspend point, and + // therefore need to spill. It also needs to mark expr scopes as having to spill. + // For example: foo() + await z + // The funtion call result of foo() must be spilled. + for (size_t i = 0; i < fn->await_list.length; i += 1) { + IrInstGenAwait *await = fn->await_list.at(i); + if (await->is_nosuspend) { + continue; + } + if (await->base.value->special != ConstValSpecialRuntime) { + // Known at comptime. No spill, no suspend. + continue; + } + if (await->target_fn != nullptr) { + // we might not need to suspend + analyze_fn_async(g, await->target_fn, false); + if (await->target_fn->anal_state == FnAnalStateInvalid) { + frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid; + return ErrorSemanticAnalyzeFail; + } + if (!fn_is_async(await->target_fn)) { + // This await does not represent a suspend point. No spill needed, + // and no need to mark ExprScope. + continue; + } + } + // This await is a suspend point, but it might not need a spill. + // We do need to mark the ExprScope as having a suspend point in it. + mark_suspension_point(await->base.base.scope); + + if (await->result_loc != nullptr) { + // If there's a result location, that is the spill + continue; + } + if (await->base.base.ref_count == 0) + continue; + if (!type_has_bits(g, await->base.value->type)) + continue; + await->result_loc = ir_create_alloca(g, await->base.base.scope, await->base.base.source_node, fn, + await->base.value->type, ""); + } + for (size_t block_i = 0; block_i < fn->analyzed_executable.basic_block_list.length; block_i += 1) { + IrBasicBlockGen *block = fn->analyzed_executable.basic_block_list.at(block_i); + for (size_t instr_i = 0; instr_i < block->instruction_list.length; instr_i += 1) { + IrInstGen *instruction = block->instruction_list.at(instr_i); + if (instruction->id == IrInstGenIdSuspendFinish) { + mark_suspension_point(instruction->base.scope); + } + } + } + // Now that we've marked all the expr scopes that have to spill, we go over the instructions + // and spill the relevant ones. + for (size_t block_i = 0; block_i < fn->analyzed_executable.basic_block_list.length; block_i += 1) { + IrBasicBlockGen *block = fn->analyzed_executable.basic_block_list.at(block_i); + for (size_t instr_i = 0; instr_i < block->instruction_list.length; instr_i += 1) { + IrInstGen *instruction = block->instruction_list.at(instr_i); + if (instruction->id == IrInstGenIdAwait || + instruction->id == IrInstGenIdVarPtr || + instruction->id == IrInstGenIdAlloca || + instruction->id == IrInstGenIdSpillBegin || + instruction->id == IrInstGenIdSpillEnd) + { + // This instruction does its own spilling specially, or otherwise doesn't need it. + continue; + } + if (instruction->id == IrInstGenIdCast && + reinterpret_cast(instruction)->cast_op == CastOpNoop) + { + // The IR instruction exists only to change the type according to Zig. No spill needed. + continue; + } + if (instruction->value->special != ConstValSpecialRuntime) + continue; + if (instruction->base.ref_count == 0) + continue; + if ((err = type_resolve(g, instruction->value->type, ResolveStatusZeroBitsKnown))) + return ErrorSemanticAnalyzeFail; + if (!type_has_bits(g, instruction->value->type)) + continue; + if (scope_needs_spill(instruction->base.scope)) { + instruction->spill = ir_create_alloca(g, instruction->base.scope, instruction->base.source_node, + fn, instruction->value->type, ""); + } + } + } + + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false); + + // label (grep this): [fn_frame_struct_layout] + ZigList fields = {}; + + fields.append({"@fn_ptr", fn_type, 0}); + fields.append({"@resume_index", g->builtin_types.entry_usize, 0}); + fields.append({"@awaiter", g->builtin_types.entry_usize, 0}); + + fields.append({"@result_ptr_callee", ptr_return_type, 0}); + fields.append({"@result_ptr_awaiter", ptr_return_type, 0}); + fields.append({"@result", fn_type_id->return_type, 0}); + + if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) { + ZigType *ptr_stack_trace_type = get_pointer_to_type(g, get_stack_trace_type(g), false); + fields.append({"@ptr_stack_trace_callee", ptr_stack_trace_type, 0}); + fields.append({"@ptr_stack_trace_awaiter", ptr_stack_trace_type, 0}); + } + + for (size_t arg_i = 0; arg_i < fn_type_id->param_count; arg_i += 1) { + FnTypeParamInfo *param_info = &fn_type_id->param_info[arg_i]; + AstNode *param_decl_node = get_param_decl_node(fn, arg_i); + Buf *param_name; + bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args; + if (param_decl_node && !is_var_args) { + param_name = param_decl_node->data.param_decl.name; + } else { + param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i); + } + ZigType *param_type = resolve_type_isf(param_info->type); + if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) { + return err; + } + + fields.append({buf_ptr(param_name), param_type, 0}); + } + + if (codegen_fn_has_err_ret_tracing_stack(g, fn, true)) { + fields.append({"@stack_trace", get_stack_trace_type(g), 0}); + fields.append({"@instruction_addresses", + get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0}); + } + + for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) { + IrInstGenAlloca *instruction = fn->alloca_gen_list.at(alloca_i); + instruction->field_index = SIZE_MAX; + ZigType *ptr_type = instruction->base.value->type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = resolve_type_isf(ptr_type->data.pointer.child_type); + if (!type_has_bits(g, child_type)) + continue; + if (instruction->base.base.ref_count == 0) + continue; + if (instruction->base.value->special != ConstValSpecialRuntime) { + if (const_ptr_pointee(nullptr, g, instruction->base.value, nullptr)->special != + ConstValSpecialRuntime) + { + continue; + } + } + + frame_type->data.frame.resolve_loop_type = child_type; + frame_type->data.frame.resolve_loop_src_node = instruction->base.base.source_node; + if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { + return err; + } + + const char *name; + if (*instruction->name_hint == 0) { + name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i)); + } else { + name = buf_ptr(buf_sprintf("%s.%" ZIG_PRI_usize, instruction->name_hint, alloca_i)); + } + instruction->field_index = fields.length; + + fields.append({name, child_type, instruction->align}); + } + + + frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name), + fields.items, fields.length, target_fn_align(g->zig_target)); + frame_type->abi_size = frame_type->data.frame.locals_struct->abi_size; + frame_type->abi_align = frame_type->data.frame.locals_struct->abi_align; + frame_type->size_in_bits = frame_type->data.frame.locals_struct->size_in_bits; + + if (g->largest_frame_fn == nullptr || frame_type->abi_size > g->largest_frame_fn->frame_type->abi_size) { + g->largest_frame_fn = fn; + } + + return ErrorNone; +} + +static Error resolve_pointer_zero_bits(CodeGen *g, ZigType *ty) { + Error err; + + if (ty->abi_size != SIZE_MAX) + return ErrorNone; + + if (ty->data.pointer.resolve_loop_flag_zero_bits) { + ty->abi_size = g->builtin_types.entry_usize->abi_size; + ty->size_in_bits = g->builtin_types.entry_usize->size_in_bits; + ty->abi_align = g->builtin_types.entry_usize->abi_align; + return ErrorNone; + } + ty->data.pointer.resolve_loop_flag_zero_bits = true; + + ZigType *elem_type; + InferredStructField *isf = ty->data.pointer.inferred_struct_field; + if (isf != nullptr) { + TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); + assert(field != nullptr); + if (field->is_comptime) { + ty->abi_size = 0; + ty->size_in_bits = 0; + ty->abi_align = 0; + return ErrorNone; + } + elem_type = field->type_entry; + } else { + elem_type = ty->data.pointer.child_type; + } + + bool has_bits; + if ((err = type_has_bits2(g, elem_type, &has_bits))) + return err; + + if (has_bits) { + ty->abi_size = g->builtin_types.entry_usize->abi_size; + ty->size_in_bits = g->builtin_types.entry_usize->size_in_bits; + ty->abi_align = g->builtin_types.entry_usize->abi_align; + } else { + ty->abi_size = 0; + ty->size_in_bits = 0; + ty->abi_align = 0; + } + return ErrorNone; +} + +Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) { + if (type_is_invalid(ty)) + return ErrorSemanticAnalyzeFail; + switch (status) { + case ResolveStatusUnstarted: + return ErrorNone; + case ResolveStatusBeingInferred: + zig_unreachable(); + case ResolveStatusInvalid: + zig_unreachable(); + case ResolveStatusZeroBitsKnown: + switch (ty->id) { + case ZigTypeIdStruct: + return resolve_struct_zero_bits(g, ty); + case ZigTypeIdEnum: + return resolve_enum_zero_bits(g, ty); + case ZigTypeIdUnion: + return resolve_union_zero_bits(g, ty); + case ZigTypeIdPointer: + return resolve_pointer_zero_bits(g, ty); + default: + return ErrorNone; + } + case ResolveStatusAlignmentKnown: + switch (ty->id) { + case ZigTypeIdStruct: + return resolve_struct_alignment(g, ty); + case ZigTypeIdEnum: + return resolve_enum_zero_bits(g, ty); + case ZigTypeIdUnion: + return resolve_union_alignment(g, ty); + case ZigTypeIdFnFrame: + return resolve_async_frame(g, ty); + case ZigTypeIdPointer: + return resolve_pointer_zero_bits(g, ty); + default: + return ErrorNone; + } + case ResolveStatusSizeKnown: + switch (ty->id) { + case ZigTypeIdStruct: + return resolve_struct_type(g, ty); + case ZigTypeIdEnum: + return resolve_enum_zero_bits(g, ty); + case ZigTypeIdUnion: + return resolve_union_type(g, ty); + case ZigTypeIdFnFrame: + return resolve_async_frame(g, ty); + case ZigTypeIdPointer: + return resolve_pointer_zero_bits(g, ty); + default: + return ErrorNone; + } + case ResolveStatusLLVMFwdDecl: + case ResolveStatusLLVMFull: + resolve_llvm_types(g, ty, status); + return ErrorNone; + } + zig_unreachable(); +} + +bool ir_get_var_is_comptime(ZigVar *var) { + if (var->is_comptime_memoized) + return var->is_comptime_memoized_value; + + var->is_comptime_memoized = true; + + // The is_comptime field can be left null, which means not comptime. + if (var->is_comptime == nullptr) { + var->is_comptime_memoized_value = false; + return var->is_comptime_memoized_value; + } + // When the is_comptime field references an instruction that has to get analyzed, this + // is the value. + if (var->is_comptime->child != nullptr) { + assert(var->is_comptime->child->value->type->id == ZigTypeIdBool); + var->is_comptime_memoized_value = var->is_comptime->child->value->data.x_bool; + var->is_comptime = nullptr; + return var->is_comptime_memoized_value; + } + // As an optimization, is_comptime values which are constant are allowed + // to be omitted from analysis. In this case, there is no child instruction + // and we simply look at the unanalyzed const parent instruction. + assert(var->is_comptime->id == IrInstSrcIdConst); + IrInstSrcConst *const_inst = reinterpret_cast(var->is_comptime); + assert(const_inst->value->type->id == ZigTypeIdBool); + var->is_comptime_memoized_value = const_inst->value->data.x_bool; + var->is_comptime = nullptr; + return var->is_comptime_memoized_value; +} + +bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { + if (a->data.x_ptr.special != b->data.x_ptr.special) + 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: + case ConstPtrSpecialSubArray: + 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; + 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 ConstPtrSpecialBaseErrorUnionCode: + if (a->data.x_ptr.data.base_err_union_code.err_union_val != + b->data.x_ptr.data.base_err_union_code.err_union_val) + { + return false; + } + return true; + case ConstPtrSpecialBaseErrorUnionPayload: + if (a->data.x_ptr.data.base_err_union_payload.err_union_val != + b->data.x_ptr.data.base_err_union_payload.err_union_val) + { + return false; + } + return true; + case ConstPtrSpecialBaseOptionalPayload: + if (a->data.x_ptr.data.base_optional_payload.optional_val != + b->data.x_ptr.data.base_optional_payload.optional_val) + { + return false; + } + return true; + case ConstPtrSpecialHardCodedAddr: + if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) + return false; + return true; + case ConstPtrSpecialDiscard: + return true; + case ConstPtrSpecialFunction: + return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; + case ConstPtrSpecialNull: + return true; + } + zig_unreachable(); +} + +static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) { + if (a->data.x_array.special == ConstArraySpecialUndef && + b->data.x_array.special == ConstArraySpecialUndef) + { + return true; + } + if (a->data.x_array.special == ConstArraySpecialUndef || + b->data.x_array.special == ConstArraySpecialUndef) + { + return false; + } + if (a->data.x_array.special == ConstArraySpecialBuf && + b->data.x_array.special == ConstArraySpecialBuf) + { + return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf); + } + expand_undef_array(g, a); + expand_undef_array(g, b); + + ZigValue *a_elems = a->data.x_array.data.s_none.elements; + ZigValue *b_elems = b->data.x_array.data.s_none.elements; + + for (size_t i = 0; i < len; i += 1) { + if (!const_values_equal(g, &a_elems[i], &b_elems[i])) + return false; + } + + return true; +} + +bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) { + if (a->type->id != b->type->id) return false; + if (a->type == b->type) { + switch (type_has_one_possible_value(g, a->type)) { + case OnePossibleValueInvalid: + zig_unreachable(); + case OnePossibleValueNo: + break; + case OnePossibleValueYes: + return true; + } + } + if (a->special == ConstValSpecialUndef || b->special == ConstValSpecialUndef) { + return a->special == b->special; + } + assert(a->special == ConstValSpecialStatic); + assert(b->special == ConstValSpecialStatic); + switch (a->type->id) { + case ZigTypeIdOpaque: + zig_unreachable(); + case ZigTypeIdEnum: + return bigint_cmp(&a->data.x_enum_tag, &b->data.x_enum_tag) == CmpEQ; + case ZigTypeIdUnion: { + ConstUnionValue *union1 = &a->data.x_union; + ConstUnionValue *union2 = &b->data.x_union; + + if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) { + TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag); + assert(field != nullptr); + if (!type_has_bits(g, field->type_entry)) + return true; + assert(find_union_field_by_tag(a->type, &union2->tag) != nullptr); + return const_values_equal(g, union1->payload, union2->payload); + } + return false; + } + case ZigTypeIdMetaType: + return a->data.x_type == b->data.x_type; + case ZigTypeIdVoid: + return true; + case ZigTypeIdErrorSet: + return a->data.x_err_set->value == b->data.x_err_set->value; + case ZigTypeIdBool: + return a->data.x_bool == b->data.x_bool; + case ZigTypeIdFloat: + assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count); + switch (a->type->data.floating.bit_count) { + case 16: + return f16_eq(a->data.x_f16, b->data.x_f16); + case 32: + return a->data.x_f32 == b->data.x_f32; + case 64: + return a->data.x_f64 == b->data.x_f64; + case 128: + return f128M_eq(&a->data.x_f128, &b->data.x_f128); + default: + zig_unreachable(); + } + case ZigTypeIdComptimeFloat: + return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; + case ZigTypeIdInt: + case ZigTypeIdComptimeInt: + return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; + case ZigTypeIdEnumLiteral: + return buf_eql_buf(a->data.x_enum_literal, b->data.x_enum_literal); + case ZigTypeIdPointer: + case ZigTypeIdFn: + return const_values_equal_ptr(a, b); + case ZigTypeIdVector: + assert(a->type->data.vector.len == b->type->data.vector.len); + return const_values_equal_array(g, a, b, a->type->data.vector.len); + case ZigTypeIdArray: { + assert(a->type->data.array.len == b->type->data.array.len); + return const_values_equal_array(g, a, b, a->type->data.array.len); + } + case ZigTypeIdStruct: + for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { + ZigValue *field_a = a->data.x_struct.fields[i]; + ZigValue *field_b = b->data.x_struct.fields[i]; + if (!const_values_equal(g, field_a, field_b)) + return false; + } + return true; + case ZigTypeIdFnFrame: + zig_panic("TODO"); + case ZigTypeIdAnyFrame: + zig_panic("TODO"); + case ZigTypeIdUndefined: + zig_panic("TODO"); + case ZigTypeIdNull: + zig_panic("TODO"); + case ZigTypeIdOptional: + if (get_src_ptr_type(a->type) != nullptr) + return const_values_equal_ptr(a, b); + if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) { + return (a->data.x_optional == nullptr && b->data.x_optional == nullptr); + } else { + return const_values_equal(g, a->data.x_optional, b->data.x_optional); + } + case ZigTypeIdErrorUnion: + zig_panic("TODO"); + case ZigTypeIdBoundFn: + case ZigTypeIdInvalid: + case ZigTypeIdUnreachable: + zig_unreachable(); + } + zig_unreachable(); +} + +void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max) { + assert(int_type->id == ZigTypeIdInt); + if (int_type->data.integral.bit_count == 0) { + bigint_init_unsigned(bigint, 0); + return; + } + if (is_max) { + // is_signed=true (1 << (bit_count - 1)) - 1 + // is_signed=false (1 << (bit_count - 0)) - 1 + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + size_t shift_amt = int_type->data.integral.bit_count - (int_type->data.integral.is_signed ? 1 : 0); + BigInt bit_count_bi = {0}; + bigint_init_unsigned(&bit_count_bi, shift_amt); + + BigInt shifted_bi = {0}; + bigint_shl(&shifted_bi, &one, &bit_count_bi); + + bigint_sub(bigint, &shifted_bi, &one); + } else if (int_type->data.integral.is_signed) { + // - (1 << (bit_count - 1)) + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + BigInt bit_count_bi = {0}; + bigint_init_unsigned(&bit_count_bi, int_type->data.integral.bit_count - 1); + + BigInt shifted_bi = {0}; + bigint_shl(&shifted_bi, &one, &bit_count_bi); + + bigint_negate(bigint, &shifted_bi); + } else { + bigint_init_unsigned(bigint, 0); + } +} + +void eval_min_max_value(CodeGen *g, ZigType *type_entry, ZigValue *const_val, bool is_max) { + if (type_entry->id == ZigTypeIdInt) { + const_val->special = ConstValSpecialStatic; + eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max); + } else if (type_entry->id == ZigTypeIdBool) { + const_val->special = ConstValSpecialStatic; + const_val->data.x_bool = is_max; + } else if (type_entry->id == ZigTypeIdVoid) { + // nothing to do + } else { + zig_unreachable(); + } +} + +static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) { + if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) { + buf_append_buf(buf, &type_entry->name); + return; + } + + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: + buf_appendf(buf, "*"); + // TODO we need a source node for const_ptr_pointee because it can generate compile errors + render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); + return; + case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: + buf_appendf(buf, "*"); + // TODO we need a source node for const_ptr_pointee because it can generate compile errors + render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); + return; + case ConstPtrSpecialHardCodedAddr: + buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name), + const_val->data.x_ptr.data.hard_coded_addr.addr); + return; + case ConstPtrSpecialDiscard: + buf_append_str(buf, "*_"); + return; + case ConstPtrSpecialFunction: + { + ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; + buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); + return; + } + case ConstPtrSpecialNull: + buf_append_str(buf, "null"); + return; + } + zig_unreachable(); +} + +static void render_const_val_err_set(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) { + if (const_val->data.x_err_set == nullptr) { + buf_append_str(buf, "null"); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); + } +} + +static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ZigValue *const_val, uint64_t start, uint64_t len) { + ConstArrayValue *array = &const_val->data.x_array; + switch (array->special) { + case ConstArraySpecialUndef: + buf_append_str(buf, "undefined"); + return; + case ConstArraySpecialBuf: { + Buf *array_buf = array->data.s_buf; + const char *base = &buf_ptr(array_buf)[start]; + assert(start + len <= buf_len(array_buf)); + + buf_append_char(buf, '"'); + for (size_t i = 0; i < len; i += 1) { + uint8_t c = base[i]; + if (c == '"') { + buf_append_str(buf, "\\\""); + } else { + buf_append_char(buf, c); + } + } + buf_append_char(buf, '"'); + return; + } + case ConstArraySpecialNone: { + assert(start + len <= const_val->type->data.array.len); + ZigValue *base = &array->data.s_none.elements[start]; + assert(len == 0 || base != nullptr); + + buf_appendf(buf, "%s{", buf_ptr(type_name)); + for (uint64_t i = 0; i < len; i += 1) { + if (i != 0) buf_appendf(buf, ","); + render_const_value(g, buf, &base[i]); + } + buf_appendf(buf, "}"); + return; + } + } + zig_unreachable(); +} + +void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) { + if (const_val == nullptr) { + buf_appendf(buf, "(invalid nullptr value)"); + return; + } + switch (const_val->special) { + case ConstValSpecialRuntime: + buf_appendf(buf, "(runtime value)"); + return; + case ConstValSpecialLazy: + buf_appendf(buf, "(lazy value)"); + return; + case ConstValSpecialUndef: + buf_appendf(buf, "undefined"); + return; + case ConstValSpecialStatic: + break; + } + assert(const_val->type); + + ZigType *type_entry = const_val->type; + switch (type_entry->id) { + case ZigTypeIdOpaque: + zig_unreachable(); + case ZigTypeIdInvalid: + buf_appendf(buf, "(invalid)"); + return; + case ZigTypeIdVoid: + buf_appendf(buf, "{}"); + return; + case ZigTypeIdComptimeFloat: + bigfloat_append_buf(buf, &const_val->data.x_bigfloat); + return; + case ZigTypeIdFloat: + switch (type_entry->data.floating.bit_count) { + case 16: + buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); + return; + case 32: + buf_appendf(buf, "%f", const_val->data.x_f32); + return; + case 64: + buf_appendf(buf, "%f", const_val->data.x_f64); + return; + case 128: + { + const size_t extra_len = 100; + size_t old_len = buf_len(buf); + buf_resize(buf, old_len + extra_len); + float64_t f64_value = f128M_to_f64(&const_val->data.x_f128); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + // TODO actual f128 printing to decimal + int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value); + assert(len > 0); + buf_resize(buf, old_len + len); + return; + } + default: + zig_unreachable(); + } + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: + bigint_append_buf(buf, &const_val->data.x_bigint, 10); + return; + case ZigTypeIdEnumLiteral: + buf_append_buf(buf, const_val->data.x_enum_literal); + return; + case ZigTypeIdMetaType: + buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name)); + return; + case ZigTypeIdUnreachable: + buf_appendf(buf, "unreachable"); + return; + case ZigTypeIdBool: + { + const char *value = const_val->data.x_bool ? "true" : "false"; + buf_appendf(buf, "%s", value); + return; + } + case ZigTypeIdFn: + { + assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); + assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); + ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; + buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name)); + return; + } + case ZigTypeIdPointer: + return render_const_val_ptr(g, buf, const_val, type_entry); + case ZigTypeIdArray: { + uint64_t len = type_entry->data.array.len; + render_const_val_array(g, buf, &type_entry->name, const_val, 0, len); + return; + } + case ZigTypeIdVector: { + uint32_t len = type_entry->data.vector.len; + render_const_val_array(g, buf, &type_entry->name, const_val, 0, len); + return; + } + case ZigTypeIdNull: + { + buf_appendf(buf, "null"); + return; + } + case ZigTypeIdUndefined: + { + buf_appendf(buf, "undefined"); + return; + } + case ZigTypeIdOptional: + { + if (get_src_ptr_type(const_val->type) != nullptr) + return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); + if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet) + return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type); + if (const_val->data.x_optional) { + render_const_value(g, buf, const_val->data.x_optional); + } else { + buf_appendf(buf, "null"); + } + return; + } + case ZigTypeIdBoundFn: + { + ZigFn *fn_entry = const_val->data.x_bound_fn.fn; + buf_appendf(buf, "(bound fn %s)", buf_ptr(&fn_entry->symbol_name)); + return; + } + case ZigTypeIdStruct: + { + if (is_slice(type_entry)) { + ZigValue *len_val = const_val->data.x_struct.fields[slice_len_index]; + size_t len = bigint_as_usize(&len_val->data.x_bigint); + + ZigValue *ptr_val = const_val->data.x_struct.fields[slice_ptr_index]; + if (ptr_val->special == ConstValSpecialUndef) { + assert(len == 0); + buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name)); + return; + } + assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); + ZigValue *array = ptr_val->data.x_ptr.data.base_array.array_val; + size_t start = ptr_val->data.x_ptr.data.base_array.elem_index; + + render_const_val_array(g, buf, &type_entry->name, array, start, len); + } else { + buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name)); + } + return; + } + case ZigTypeIdEnum: + { + TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag); + if(field != nullptr){ + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name)); + } else { + // untagged value in a non-exhaustive enum + buf_appendf(buf, "%s.(", buf_ptr(&type_entry->name)); + bigint_append_buf(buf, &const_val->data.x_enum_tag, 10); + buf_appendf(buf, ")"); + } + return; + } + case ZigTypeIdErrorUnion: + { + buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set == nullptr) { + render_const_value(g, buf, const_val->data.x_err_union.payload); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), + buf_ptr(&err_set->name)); + } + buf_appendf(buf, ")"); + return; + } + case ZigTypeIdUnion: + { + const BigInt *tag = &const_val->data.x_union.tag; + TypeUnionField *field = find_union_field_by_tag(type_entry, tag); + buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name)); + render_const_value(g, buf, const_val->data.x_union.payload); + buf_append_str(buf, "}"); + return; + } + case ZigTypeIdErrorSet: + return render_const_val_err_set(g, buf, const_val, type_entry); + case ZigTypeIdFnFrame: + buf_appendf(buf, "(TODO: async function frame value)"); + return; + + case ZigTypeIdAnyFrame: + buf_appendf(buf, "(TODO: anyframe value)"); + return; + + } + zig_unreachable(); +} + +ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { + assert(size_in_bits <= 65535); + ZigType *entry = new_type_table_entry(ZigTypeIdInt); + + entry->size_in_bits = size_in_bits; + if (size_in_bits != 0) { + entry->llvm_type = LLVMIntType(size_in_bits); + entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); + entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); + + if (size_in_bits >= 128 && entry->abi_align < 16) { + // Override the incorrect alignment reported by LLVM. Clang does this as well. + // On x86_64 there are some instructions like CMPXCHG16B which require this. + // On all targets, integers 128 bits and above have ABI alignment of 16. + // However for some targets, LLVM incorrectly reports this as 8. + // See: https://github.com/ziglang/zig/issues/2987 + entry->abi_align = 16; + } + } + + const char u_or_i = is_signed ? 'i' : 'u'; + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "%c%" PRIu32, u_or_i, size_in_bits); + + 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 ZigTypeIdInvalid: + case ZigTypeIdOpaque: + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdFloat: + case ZigTypeIdStruct: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + zig_unreachable(); + case ZigTypeIdErrorUnion: + return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); + case ZigTypeIdPointer: + return hash_ptr(x.data.pointer.child_type) + + (uint32_t)x.data.pointer.ptr_len * 1120226602u + + (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + + (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) + + (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) + + (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) + + (((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) + + (((uint32_t)x.data.pointer.vector_index) ^ (uint32_t)0x19199716) + + (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881) * + (x.data.pointer.sentinel ? hash_const_val(x.data.pointer.sentinel) : (uint32_t)2955491856); + case ZigTypeIdArray: + return hash_ptr(x.data.array.child_type) * + ((uint32_t)x.data.array.size ^ (uint32_t)2122979968) * + (x.data.array.sentinel ? hash_const_val(x.data.array.sentinel) : (uint32_t)1927201585); + case ZigTypeIdInt: + return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); + case ZigTypeIdVector: + return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681); + } + zig_unreachable(); +} + +bool type_id_eql(TypeId a, TypeId b) { + if (a.id != b.id) + return false; + switch (a.id) { + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdFloat: + case ZigTypeIdStruct: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + zig_unreachable(); + case ZigTypeIdErrorUnion: + return a.data.error_union.err_set_type == b.data.error_union.err_set_type && + a.data.error_union.payload_type == b.data.error_union.payload_type; + + case ZigTypeIdPointer: + return a.data.pointer.child_type == b.data.pointer.child_type && + a.data.pointer.ptr_len == b.data.pointer.ptr_len && + a.data.pointer.is_const == b.data.pointer.is_const && + a.data.pointer.is_volatile == b.data.pointer.is_volatile && + a.data.pointer.allow_zero == b.data.pointer.allow_zero && + a.data.pointer.alignment == b.data.pointer.alignment && + a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host && + a.data.pointer.vector_index == b.data.pointer.vector_index && + a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes && + ( + a.data.pointer.sentinel == b.data.pointer.sentinel || + (a.data.pointer.sentinel != nullptr && b.data.pointer.sentinel != nullptr && + const_values_equal(a.data.pointer.codegen, a.data.pointer.sentinel, b.data.pointer.sentinel)) + ) && + ( + a.data.pointer.inferred_struct_field == b.data.pointer.inferred_struct_field || + (a.data.pointer.inferred_struct_field != nullptr && + b.data.pointer.inferred_struct_field != nullptr && + a.data.pointer.inferred_struct_field->inferred_struct_type == + b.data.pointer.inferred_struct_field->inferred_struct_type && + buf_eql_buf(a.data.pointer.inferred_struct_field->field_name, + b.data.pointer.inferred_struct_field->field_name)) + ); + case ZigTypeIdArray: + return a.data.array.child_type == b.data.array.child_type && + a.data.array.size == b.data.array.size && + ( + a.data.array.sentinel == b.data.array.sentinel || + (a.data.array.sentinel != nullptr && b.data.array.sentinel != nullptr && + const_values_equal(a.data.array.codegen, a.data.array.sentinel, b.data.array.sentinel)) + ); + case ZigTypeIdInt: + return a.data.integer.is_signed == b.data.integer.is_signed && + a.data.integer.bit_count == b.data.integer.bit_count; + case ZigTypeIdVector: + return a.data.vector.elem_type == b.data.vector.elem_type && + a.data.vector.len == b.data.vector.len; + } + zig_unreachable(); +} + +uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { + switch (x.id) { + case ZigLLVMFnIdCtz: + return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934; + case ZigLLVMFnIdClz: + return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; + case ZigLLVMFnIdPopCount: + return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049; + case ZigLLVMFnIdFloatOp: + return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) + + (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) + + (uint32_t)(x.data.floating.op) * (uint32_t)43789879; + case ZigLLVMFnIdFMA: + return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) + + (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025); + case ZigLLVMFnIdBswap: + return (uint32_t)(x.data.bswap.bit_count) * ((uint32_t)3661994335) + + (uint32_t)(x.data.bswap.vector_len) * (((uint32_t)x.id << 5) + 1025); + case ZigLLVMFnIdBitReverse: + return (uint32_t)(x.data.bit_reverse.bit_count) * (uint32_t)2621398431; + case ZigLLVMFnIdOverflowArithmetic: + return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) + + ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) + + ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) + + x.data.overflow_arithmetic.vector_len * 1435156945; + } + 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 ZigLLVMFnIdPopCount: + return a.data.pop_count.bit_count == b.data.pop_count.bit_count; + case ZigLLVMFnIdBswap: + return a.data.bswap.bit_count == b.data.bswap.bit_count && + a.data.bswap.vector_len == b.data.bswap.vector_len; + case ZigLLVMFnIdBitReverse: + return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count; + case ZigLLVMFnIdFloatOp: + return a.data.floating.bit_count == b.data.floating.bit_count && + a.data.floating.vector_len == b.data.floating.vector_len && + a.data.floating.op == b.data.floating.op; + case ZigLLVMFnIdFMA: + return a.data.floating.bit_count == b.data.floating.bit_count && + a.data.floating.vector_len == b.data.floating.vector_len; + 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) && + (a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len); + } + zig_unreachable(); +} + +static void init_const_undefined(CodeGen *g, ZigValue *const_val) { + Error err; + ZigType *wanted_type = const_val->type; + if (wanted_type->id == ZigTypeIdArray) { + const_val->special = ConstValSpecialStatic; + const_val->data.x_array.special = ConstArraySpecialUndef; + } else if (wanted_type->id == ZigTypeIdStruct) { + if ((err = type_resolve(g, wanted_type, ResolveStatusZeroBitsKnown))) { + return; + } + + const_val->special = ConstValSpecialStatic; + size_t field_count = wanted_type->data.structure.src_field_count; + const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count); + for (size_t i = 0; i < field_count; i += 1) { + ZigValue *field_val = const_val->data.x_struct.fields[i]; + field_val->type = resolve_struct_field_type(g, wanted_type->data.structure.fields[i]); + assert(field_val->type); + init_const_undefined(g, field_val); + field_val->parent.id = ConstParentIdStruct; + field_val->parent.data.p_struct.struct_val = const_val; + field_val->parent.data.p_struct.field_index = i; + } + } else { + const_val->special = ConstValSpecialUndef; + } +} + +void expand_undef_struct(CodeGen *g, ZigValue *const_val) { + if (const_val->special == ConstValSpecialUndef) { + init_const_undefined(g, const_val); + } +} + +// Canonicalize the array value as ConstArraySpecialNone +void expand_undef_array(CodeGen *g, ZigValue *const_val) { + size_t elem_count; + ZigType *elem_type; + if (const_val->type->id == ZigTypeIdArray) { + elem_count = const_val->type->data.array.len; + elem_type = const_val->type->data.array.child_type; + } else if (const_val->type->id == ZigTypeIdVector) { + elem_count = const_val->type->data.vector.len; + elem_type = const_val->type->data.vector.elem_type; + } else { + zig_unreachable(); + } + if (const_val->special == ConstValSpecialUndef) { + const_val->special = ConstValSpecialStatic; + const_val->data.x_array.special = ConstArraySpecialUndef; + } + switch (const_val->data.x_array.special) { + case ConstArraySpecialNone: + return; + case ConstArraySpecialUndef: { + const_val->data.x_array.special = ConstArraySpecialNone; + const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count); + for (size_t i = 0; i < elem_count; i += 1) { + ZigValue *element_val = &const_val->data.x_array.data.s_none.elements[i]; + element_val->type = elem_type; + init_const_undefined(g, element_val); + element_val->parent.id = ConstParentIdArray; + element_val->parent.data.p_array.array_val = const_val; + element_val->parent.data.p_array.elem_index = i; + } + return; + } + case ConstArraySpecialBuf: { + Buf *buf = const_val->data.x_array.data.s_buf; + // If we're doing this it means that we are potentially modifying the data, + // so we can't have it be in the string literals table + g->string_literals_table.maybe_remove(buf); + + const_val->data.x_array.special = ConstArraySpecialNone; + assert(elem_count == buf_len(buf)); + const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count); + for (size_t i = 0; i < elem_count; i += 1) { + ZigValue *this_char = &const_val->data.x_array.data.s_none.elements[i]; + this_char->special = ConstValSpecialStatic; + this_char->type = g->builtin_types.entry_u8; + bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]); + this_char->parent.id = ConstParentIdArray; + this_char->parent.data.p_array.array_val = const_val; + this_char->parent.data.p_array.elem_index = i; + } + return; + } + } + zig_unreachable(); +} + +static const ZigTypeId all_type_ids[] = { + ZigTypeIdMetaType, + ZigTypeIdVoid, + ZigTypeIdBool, + ZigTypeIdUnreachable, + ZigTypeIdInt, + ZigTypeIdFloat, + ZigTypeIdPointer, + ZigTypeIdArray, + ZigTypeIdStruct, + ZigTypeIdComptimeFloat, + ZigTypeIdComptimeInt, + ZigTypeIdUndefined, + ZigTypeIdNull, + ZigTypeIdOptional, + ZigTypeIdErrorUnion, + ZigTypeIdErrorSet, + ZigTypeIdEnum, + ZigTypeIdUnion, + ZigTypeIdFn, + ZigTypeIdBoundFn, + ZigTypeIdOpaque, + ZigTypeIdFnFrame, + ZigTypeIdAnyFrame, + ZigTypeIdVector, + ZigTypeIdEnumLiteral, +}; + +ZigTypeId type_id_at_index(size_t index) { + assert(index < array_length(all_type_ids)); + return all_type_ids[index]; +} + +size_t type_id_len() { + return array_length(all_type_ids); +} + +size_t type_id_index(ZigType *entry) { + switch (entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + return 0; + case ZigTypeIdVoid: + return 1; + case ZigTypeIdBool: + return 2; + case ZigTypeIdUnreachable: + return 3; + case ZigTypeIdInt: + return 4; + case ZigTypeIdFloat: + return 5; + case ZigTypeIdPointer: + return 6; + case ZigTypeIdArray: + return 7; + case ZigTypeIdStruct: + if (entry->data.structure.special == StructSpecialSlice) + return 6; + return 8; + case ZigTypeIdComptimeFloat: + return 9; + case ZigTypeIdComptimeInt: + return 10; + case ZigTypeIdUndefined: + return 11; + case ZigTypeIdNull: + return 12; + case ZigTypeIdOptional: + return 13; + case ZigTypeIdErrorUnion: + return 14; + case ZigTypeIdErrorSet: + return 15; + case ZigTypeIdEnum: + return 16; + case ZigTypeIdUnion: + return 17; + case ZigTypeIdFn: + return 18; + case ZigTypeIdBoundFn: + return 19; + case ZigTypeIdOpaque: + return 20; + case ZigTypeIdFnFrame: + return 21; + case ZigTypeIdAnyFrame: + return 22; + case ZigTypeIdVector: + return 23; + case ZigTypeIdEnumLiteral: + return 24; + } + zig_unreachable(); +} + +const char *type_id_name(ZigTypeId id) { + switch (id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + return "Type"; + case ZigTypeIdVoid: + return "Void"; + case ZigTypeIdBool: + return "Bool"; + case ZigTypeIdUnreachable: + return "NoReturn"; + case ZigTypeIdInt: + return "Int"; + case ZigTypeIdFloat: + return "Float"; + case ZigTypeIdPointer: + return "Pointer"; + case ZigTypeIdArray: + return "Array"; + case ZigTypeIdStruct: + return "Struct"; + case ZigTypeIdComptimeFloat: + return "ComptimeFloat"; + case ZigTypeIdComptimeInt: + return "ComptimeInt"; + case ZigTypeIdEnumLiteral: + return "EnumLiteral"; + case ZigTypeIdUndefined: + return "Undefined"; + case ZigTypeIdNull: + return "Null"; + case ZigTypeIdOptional: + return "Optional"; + case ZigTypeIdErrorUnion: + return "ErrorUnion"; + case ZigTypeIdErrorSet: + return "ErrorSet"; + case ZigTypeIdEnum: + return "Enum"; + case ZigTypeIdUnion: + return "Union"; + case ZigTypeIdFn: + return "Fn"; + case ZigTypeIdBoundFn: + return "BoundFn"; + case ZigTypeIdOpaque: + return "Opaque"; + case ZigTypeIdVector: + return "Vector"; + case ZigTypeIdFnFrame: + return "Frame"; + case ZigTypeIdAnyFrame: + return "AnyFrame"; + } + zig_unreachable(); +} + +ZigType *get_align_amt_type(CodeGen *g) { + if (g->align_amt_type == nullptr) { + // according to LLVM the maximum alignment is 1 << 29. + g->align_amt_type = get_int_type(g, false, 29); + } + return g->align_amt_type; +} + +uint32_t type_ptr_hash(const ZigType *ptr) { + return hash_ptr((void*)ptr); +} + +bool type_ptr_eql(const ZigType *a, const ZigType *b) { + return a == b; +} + +uint32_t pkg_ptr_hash(const ZigPackage *ptr) { + return hash_ptr((void*)ptr); +} + +bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b) { + return a == b; +} + +uint32_t tld_ptr_hash(const Tld *ptr) { + return hash_ptr((void*)ptr); +} + +bool tld_ptr_eql(const Tld *a, const Tld *b) { + return a == b; +} + +uint32_t node_ptr_hash(const AstNode *ptr) { + return hash_ptr((void*)ptr); +} + +bool node_ptr_eql(const AstNode *a, const AstNode *b) { + return a == b; +} + +uint32_t fn_ptr_hash(const ZigFn *ptr) { + return hash_ptr((void*)ptr); +} + +bool fn_ptr_eql(const ZigFn *a, const ZigFn *b) { + return a == b; +} + +uint32_t err_ptr_hash(const ErrorTableEntry *ptr) { + return hash_ptr((void*)ptr); +} + +bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b) { + return a == b; +} + +ZigValue *get_builtin_value(CodeGen *codegen, const char *name) { + ScopeDecls *builtin_scope = get_container_scope(codegen->compile_var_import); + Tld *tld = find_container_decl(codegen, builtin_scope, buf_create_from_str(name)); + assert(tld != nullptr); + resolve_top_level_decl(codegen, tld, nullptr, false); + assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk); + TldVar *tld_var = (TldVar *)tld; + ZigValue *var_value = tld_var->var->const_value; + assert(var_value != nullptr); + return var_value; +} + +ZigType *get_builtin_type(CodeGen *codegen, const char *name) { + ZigValue *type_val = get_builtin_value(codegen, name); + assert(type_val->type->id == ZigTypeIdMetaType); + return type_val->data.x_type; +} + +bool type_is_global_error_set(ZigType *err_set_type) { + assert(err_set_type->id == ZigTypeIdErrorSet); + assert(!err_set_type->data.error_set.incomplete); + return err_set_type->data.error_set.err_count == UINT32_MAX; +} + +bool type_can_fail(ZigType *type_entry) { + return type_entry->id == ZigTypeIdErrorUnion || type_entry->id == ZigTypeIdErrorSet; +} + +bool fn_type_can_fail(FnTypeId *fn_type_id) { + return type_can_fail(fn_type_id->return_type); +} + +// ErrorNone - result pointer has the type +// ErrorOverflow - an integer primitive type has too large a bit width +// ErrorPrimitiveTypeNotFound - result pointer unchanged +Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result) { + if (buf_len(name) >= 2) { + uint8_t first_c = buf_ptr(name)[0]; + if (first_c == 'i' || first_c == 'u') { + for (size_t i = 1; i < buf_len(name); i += 1) { + uint8_t c = buf_ptr(name)[i]; + if (c < '0' || c > '9') { + goto not_integer; + } + } + bool is_signed = (first_c == 'i'); + unsigned long int bit_count = strtoul(buf_ptr(name) + 1, nullptr, 10); + // strtoul returns ULONG_MAX on errors, so this comparison catches that as well. + if (bit_count >= 65536) return ErrorOverflow; + *result = get_int_type(g, is_signed, bit_count); + return ErrorNone; + } + } + +not_integer: + + auto primitive_table_entry = g->primitive_type_table.maybe_get(name); + if (primitive_table_entry == nullptr) + return ErrorPrimitiveTypeNotFound; + + *result = primitive_table_entry->value; + return ErrorNone; +} + +Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents_buf) { + size_t len; + const char *contents = stage2_fetch_file(&g->stage1, buf_ptr(resolved_path), buf_len(resolved_path), &len); + if (contents == nullptr) + return ErrorFileNotFound; + buf_init_from_mem(contents_buf, contents, len); + return ErrorNone; +} + +static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) { + // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017 + switch (ty->id) { + case ZigTypeIdEnum: + case ZigTypeIdInt: + case ZigTypeIdBool: + return X64CABIClass_INTEGER; + case ZigTypeIdFloat: + case ZigTypeIdVector: + return X64CABIClass_SSE; + case ZigTypeIdStruct: + case ZigTypeIdUnion: { + if (ty_size <= 8) + return X64CABIClass_INTEGER; + return X64CABIClass_MEMORY; + } + default: + return X64CABIClass_Unknown; + } +} + +static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) { + switch (ty->id) { + case ZigTypeIdEnum: + case ZigTypeIdInt: + case ZigTypeIdBool: + return X64CABIClass_INTEGER; + case ZigTypeIdFloat: + case ZigTypeIdVector: + return X64CABIClass_SSE; + case ZigTypeIdStruct: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.structure.layout != ContainerLayoutExtern) { + // TODO determine whether packed structs have any unaligned fields + return X64CABIClass_Unknown; + } + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[0]->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + case ZigTypeIdUnion: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.unionation.layout != ContainerLayoutExtern) + return X64CABIClass_MEMORY; + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + default: + return X64CABIClass_Unknown; + } +} + +X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { + Error err; + + const size_t ty_size = type_size(g, ty); + ZigType *ptr_type; + if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return X64CABIClass_Unknown; + if (ptr_type != nullptr) + return X64CABIClass_INTEGER; + + if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) { + return type_windows_abi_x86_64_class(g, ty, ty_size); + } else if (g->zig_target->arch == ZigLLVM_aarch64 || + g->zig_target->arch == ZigLLVM_aarch64_be) + { + X64CABIClass result = type_system_V_abi_x86_64_class(g, ty, ty_size); + return (result == X64CABIClass_MEMORY) ? X64CABIClass_MEMORY_nobyval : result; + } else { + return type_system_V_abi_x86_64_class(g, ty, ty_size); + } +} + +// NOTE this does not depend on x86_64 +Error type_is_c_abi_int(CodeGen *g, ZigType *ty, bool *result) { + if (ty->id == ZigTypeIdInt || + ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdBool || + ty->id == ZigTypeIdEnum || + ty->id == ZigTypeIdVoid || + ty->id == ZigTypeIdUnreachable) + { + *result = true; + return ErrorNone; + } + + Error err; + ZigType *ptr_type; + if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return err; + *result = ptr_type != nullptr; + return ErrorNone; +} + +bool type_is_c_abi_int_bail(CodeGen *g, ZigType *ty) { + Error err; + bool result; + if ((err = type_is_c_abi_int(g, ty, &result))) + codegen_report_errors_and_exit(g); + + return result; +} + +uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) { + assert(struct_type->id == ZigTypeIdStruct); + if (struct_type->data.structure.layout != ContainerLayoutAuto) { + assert(type_is_resolved(struct_type, ResolveStatusSizeKnown)); + } + if (struct_type->data.structure.host_int_bytes == nullptr) + return 0; + return struct_type->data.structure.host_int_bytes[field->gen_index]; +} + +Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, + ZigValue *const_val, ZigType *wanted_type) +{ + ZigValue ptr_val = {}; + ptr_val.special = ConstValSpecialStatic; + ptr_val.type = get_pointer_to_type(codegen, wanted_type, true); + ptr_val.data.x_ptr.mut = ConstPtrMutComptimeConst; + ptr_val.data.x_ptr.special = ConstPtrSpecialRef; + ptr_val.data.x_ptr.data.ref.pointee = const_val; + if (const_ptr_pointee(ira, codegen, &ptr_val, source_node) == nullptr) + return ErrorSemanticAnalyzeFail; + + return ErrorNone; +} + +const char *container_string(ContainerKind kind) { + switch (kind) { + case ContainerKindEnum: return "enum"; + case ContainerKindStruct: return "struct"; + case ContainerKindUnion: return "union"; + } + zig_unreachable(); +} + +bool ptr_allows_addr_zero(ZigType *ptr_type) { + if (ptr_type->id == ZigTypeIdPointer) { + return ptr_type->data.pointer.allow_zero; + } else if (ptr_type->id == ZigTypeIdOptional) { + return true; + } + return false; +} + +Buf *type_bare_name(ZigType *type_entry) { + if (is_slice(type_entry)) { + return &type_entry->name; + } else if (is_container(type_entry)) { + return get_container_scope(type_entry)->bare_name; + } else if (type_entry->id == ZigTypeIdOpaque) { + return type_entry->data.opaque.bare_name; + } else { + return &type_entry->name; + } +} + +// TODO this will have to be more clever, probably using the full name +// and replacing '.' with '_' or something like that +Buf *type_h_name(ZigType *t) { + return type_bare_name(t); +} + +static void resolve_llvm_types_slice(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { + if (type->data.structure.resolve_status >= wanted_resolve_status) return; + + ZigType *ptr_type = type->data.structure.fields[slice_ptr_index]->type_entry; + ZigType *child_type = ptr_type->data.pointer.child_type; + ZigType *usize_type = g->builtin_types.entry_usize; + + bool done = false; + if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || + ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero || + ptr_type->data.pointer.sentinel != nullptr) + { + ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, + PtrLenUnknown, 0, 0, 0, false); + ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type); + + assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status)); + type->llvm_type = peer_slice_type->llvm_type; + type->llvm_di_type = peer_slice_type->llvm_di_type; + type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status; + done = true; + } + + // 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)) { + ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index]->type_entry; + assert(child_ptr_type->id == ZigTypeIdPointer); + if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile || + child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero || + child_ptr_type->data.pointer.sentinel != nullptr) + { + ZigType *grand_child_type = child_ptr_type->data.pointer.child_type; + ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false, + PtrLenUnknown, 0, 0, 0, false); + ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type); + ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false, + PtrLenUnknown, 0, 0, 0, false); + ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type); + + assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status)); + type->llvm_type = peer_slice_type->llvm_type; + type->llvm_di_type = peer_slice_type->llvm_di_type; + type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status; + done = true; + } + } + + if (done) return; + + LLVMTypeRef usize_llvm_type = get_llvm_type(g, usize_type); + ZigLLVMDIType *usize_llvm_di_type = get_llvm_di_type(g, usize_type); + ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); + ZigLLVMDIFile *di_file = nullptr; + unsigned line = 0; + + if (type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) { + type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name)); + + type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name), + compile_unit_scope, di_file, line); + + type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl; + if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return; + } + + if (!type_has_bits(g, child_type)) { + LLVMTypeRef element_types[] = { + usize_llvm_type, + }; + LLVMStructSetBody(type->llvm_type, element_types, 1, false); + + uint64_t len_debug_size_in_bits = usize_type->size_in_bits; + uint64_t len_debug_align_in_bits = 8*usize_type->abi_align; + uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0); + + uint64_t debug_size_in_bits = type->size_in_bits; + uint64_t debug_align_in_bits = 8*type->abi_align; + + ZigLLVMDIType *di_element_types[] = { + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), + "len", di_file, line, + len_debug_size_in_bits, + len_debug_align_in_bits, + len_offset_in_bits, + ZigLLVM_DIFlags_Zero, + usize_llvm_di_type), + }; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&type->name), + di_file, line, debug_size_in_bits, debug_align_in_bits, + ZigLLVM_DIFlags_Zero, + nullptr, di_element_types, 1, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type); + type->llvm_di_type = replacement_di_type; + type->data.structure.resolve_status = ResolveStatusLLVMFull; + return; + } + + LLVMTypeRef element_types[2]; + element_types[slice_ptr_index] = get_llvm_type(g, ptr_type); + element_types[slice_len_index] = get_llvm_type(g, g->builtin_types.entry_usize); + if (type->data.structure.resolve_status >= wanted_resolve_status) return; + LLVMStructSetBody(type->llvm_type, element_types, 2, false); + + uint64_t ptr_debug_size_in_bits = ptr_type->size_in_bits; + uint64_t ptr_debug_align_in_bits = 8*ptr_type->abi_align; + uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0); + + uint64_t len_debug_size_in_bits = usize_type->size_in_bits; + uint64_t len_debug_align_in_bits = 8*usize_type->abi_align; + uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1); + + uint64_t debug_size_in_bits = type->size_in_bits; + uint64_t debug_align_in_bits = 8*type->abi_align; + + ZigLLVMDIType *di_element_types[] = { + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), + "ptr", di_file, line, + ptr_debug_size_in_bits, + ptr_debug_align_in_bits, + ptr_offset_in_bits, + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_type)), + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), + "len", di_file, line, + len_debug_size_in_bits, + len_debug_align_in_bits, + len_offset_in_bits, + ZigLLVM_DIFlags_Zero, usize_llvm_di_type), + }; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&type->name), + di_file, line, debug_size_in_bits, debug_align_in_bits, + ZigLLVM_DIFlags_Zero, + nullptr, di_element_types, 2, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type); + type->llvm_di_type = replacement_di_type; + type->data.structure.resolve_status = ResolveStatusLLVMFull; +} + +static LLVMTypeRef get_llvm_type_of_n_bytes(unsigned byte_size) { + return byte_size == 1 ? + LLVMInt8Type() : LLVMArrayType(LLVMInt8Type(), byte_size); +} + +static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveStatus wanted_resolve_status, + ZigType *async_frame_type) +{ + assert(struct_type->id == ZigTypeIdStruct); + assert(struct_type->data.structure.resolve_status != ResolveStatusInvalid); + assert(struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown); + assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0); + if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return; + + AstNode *decl_node = struct_type->data.structure.decl_node; + ZigLLVMDIFile *di_file; + ZigLLVMDIScope *di_scope; + unsigned line; + if (decl_node != nullptr) { + Scope *scope = &struct_type->data.structure.decls_scope->base; + ZigType *import = get_scope_import(scope); + di_file = import->data.structure.root_struct->di_file; + di_scope = ZigLLVMFileToScope(di_file); + line = decl_node->line + 1; + } else { + di_file = nullptr; + di_scope = ZigLLVMCompileUnitToScope(g->compile_unit); + line = 0; + } + + if (struct_type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) { + struct_type->llvm_type = type_has_bits(g, struct_type) ? + LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&struct_type->name)) : LLVMVoidType(); + unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); + struct_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + dwarf_kind, buf_ptr(&struct_type->name), + di_scope, di_file, line); + + struct_type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl; + if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) { + struct_type->data.structure.llvm_full_type_queue_index = g->type_resolve_stack.length; + g->type_resolve_stack.append(struct_type); + return; + } else { + struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX; + } + } + + size_t field_count = struct_type->data.structure.src_field_count; + // Every field could potentially have a generated padding field after it. + LLVMTypeRef *element_types = heap::c_allocator.allocate(field_count * 2); + + 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; + + // trigger all the recursive get_llvm_type calls + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + ZigType *field_type = field->type_entry; + if (!type_has_bits(g, field_type)) + continue; + (void)get_llvm_type(g, field_type); + if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return; + } + + size_t gen_field_index = 0; + + // Calculate what LLVM thinks the ABI align of the struct will be. We do this to avoid + // inserting padding bytes where LLVM would do it automatically. + size_t llvm_struct_abi_align = 0; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + ZigType *field_type = field->type_entry; + if (field->is_comptime || !type_has_bits(g, field_type)) + continue; + LLVMTypeRef field_llvm_type = get_llvm_type(g, field_type); + size_t llvm_field_abi_align = LLVMABIAlignmentOfType(g->target_data_ref, field_llvm_type); + llvm_struct_abi_align = max(llvm_struct_abi_align, llvm_field_abi_align); + } + + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + ZigType *field_type = field->type_entry; + + if (field->is_comptime || !type_has_bits(g, field_type)) { + field->gen_index = SIZE_MAX; + continue; + } + + if (packed) { + 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; + + 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 + + size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign; + size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes); + if (full_abi_size * 8 == full_bit_count) { + // next field recovers ABI alignment + element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size); + gen_field_index += 1; + first_packed_bits_offset_misalign = SIZE_MAX; + } + } else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) { + first_packed_bits_offset_misalign = packed_bits_offset; + } else { + // This is a byte-aligned field (both start and end) in a packed struct. + element_types[gen_field_index] = get_llvm_type(g, field_type); + assert(get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) == + LLVMStoreSizeOfType(g->target_data_ref, element_types[gen_field_index])); + gen_field_index += 1; + } + packed_bits_offset = next_packed_bits_offset; + } else { + LLVMTypeRef llvm_type; + if (i == 0 && async_frame_type != nullptr) { + assert(async_frame_type->id == ZigTypeIdFnFrame); + assert(field_type->id == ZigTypeIdFn); + resolve_llvm_types_fn(g, async_frame_type->data.frame.fn); + llvm_type = LLVMPointerType(async_frame_type->data.frame.fn->raw_type_ref, 0); + } else { + llvm_type = get_llvm_type(g, field_type); + } + element_types[gen_field_index] = llvm_type; + field->gen_index = gen_field_index; + gen_field_index += 1; + + // find the next non-zero-byte field for offset calculations + size_t next_src_field_index = i + 1; + for (; next_src_field_index < field_count; next_src_field_index += 1) { + if (type_has_bits(g, struct_type->data.structure.fields[next_src_field_index]->type_entry)) + break; + } + size_t next_abi_align; + if (next_src_field_index == field_count) { + next_abi_align = struct_type->abi_align; + } else { + if (struct_type->data.structure.fields[next_src_field_index]->align == 0) { + next_abi_align = struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align; + } else { + next_abi_align = struct_type->data.structure.fields[next_src_field_index]->align; + } + } + size_t llvm_next_abi_align = (next_src_field_index == field_count) ? + llvm_struct_abi_align : + LLVMABIAlignmentOfType(g->target_data_ref, + get_llvm_type(g, struct_type->data.structure.fields[next_src_field_index]->type_entry)); + + size_t next_offset = next_field_offset(field->offset, struct_type->abi_align, + field_type->abi_size, next_abi_align); + size_t llvm_next_offset = next_field_offset(field->offset, llvm_struct_abi_align, + LLVMABISizeOfType(g->target_data_ref, llvm_type), llvm_next_abi_align); + + assert(next_offset >= llvm_next_offset); + if (next_offset > llvm_next_offset) { + size_t pad_bytes = next_offset - (field->offset + LLVMStoreSizeOfType(g->target_data_ref, llvm_type)); + if (pad_bytes != 0) { + LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes); + element_types[gen_field_index] = pad_llvm_type; + gen_field_index += 1; + } + } + } + debug_field_count += 1; + } + if (!packed) { + struct_type->data.structure.gen_field_count = gen_field_index; + } + + if (first_packed_bits_offset_misalign != SIZE_MAX) { + size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign; + size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes); + element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size); + gen_field_index += 1; + } + + if (type_has_bits(g, struct_type)) { + assert(struct_type->data.structure.gen_field_count == gen_field_index); + LLVMStructSetBody(struct_type->llvm_type, element_types, + (unsigned)struct_type->data.structure.gen_field_count, packed); + } + + ZigLLVMDIType **di_element_types = heap::c_allocator.allocate(debug_field_count); + size_t debug_field_index = 0; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + //fprintf(stderr, "%s at gen index %zu\n", buf_ptr(field->name), field->gen_index); + + size_t gen_field_index = field->gen_index; + if (gen_field_index == SIZE_MAX) { + continue; + } + + ZigType *field_type = 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 == ZigTypeIdFn) { + ZigType *field_ptr_type = get_pointer_to_type(g, field_type, true); + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type)); + uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type)); + field_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, field_type), + debug_size_in_bits, debug_align_in_bits, buf_ptr(&field_ptr_type->name)); + } else { + field_di_type = get_llvm_di_type(g, field_type); + } + + uint64_t debug_size_in_bits; + uint64_t debug_align_in_bits; + uint64_t debug_offset_in_bits; + if (packed) { + debug_size_in_bits = field->type_entry->size_in_bits; + debug_align_in_bits = 8 * field->type_entry->abi_align; + debug_offset_in_bits = 8 * field->offset + field->bit_offset_in_host; + } else { + debug_size_in_bits = 8 * get_store_size_bytes(field_type->size_in_bits); + debug_align_in_bits = 8 * field_type->abi_align; + debug_offset_in_bits = 8 * field->offset; + } + unsigned line; + if (decl_node != nullptr) { + AstNode *field_node = field->decl_node; + line = field_node->line + 1; + } else { + line = 0; + } + di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(struct_type->llvm_di_type), buf_ptr(field->name), + di_file, line, + debug_size_in_bits, + debug_align_in_bits, + debug_offset_in_bits, + ZigLLVM_DIFlags_Zero, field_di_type); + assert(di_element_types[debug_field_index]); + debug_field_index += 1; + } + + uint64_t debug_size_in_bits = 8*get_store_size_bytes(struct_type->size_in_bits); + uint64_t debug_align_in_bits = 8*struct_type->abi_align; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + di_scope, + buf_ptr(&struct_type->name), + di_file, line, + debug_size_in_bits, + debug_align_in_bits, + ZigLLVM_DIFlags_Zero, + nullptr, di_element_types, (int)debug_field_count, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, struct_type->llvm_di_type, replacement_di_type); + struct_type->llvm_di_type = replacement_di_type; + struct_type->data.structure.resolve_status = ResolveStatusLLVMFull; + if (struct_type->data.structure.llvm_full_type_queue_index != SIZE_MAX) { + ZigType *last = g->type_resolve_stack.last(); + assert(last->id == ZigTypeIdStruct); + last->data.structure.llvm_full_type_queue_index = struct_type->data.structure.llvm_full_type_queue_index; + g->type_resolve_stack.swap_remove(struct_type->data.structure.llvm_full_type_queue_index); + struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX; + } +} + +// This is to be used instead of void for debug info types, to avoid tripping +// Assertion `!isa(Scope) && "shouldn't make a namespace scope for a type"' +// when targeting CodeView (Windows). +static ZigLLVMDIType *make_empty_namespace_llvm_di_type(CodeGen *g, ZigType *import, const char *name, + AstNode *decl_node) +{ + uint64_t debug_size_in_bits = 0; + uint64_t debug_align_in_bits = 0; + ZigLLVMDIType **di_element_types = nullptr; + size_t debug_field_count = 0; + return ZigLLVMCreateDebugStructType(g->dbuilder, + ZigLLVMFileToScope(import->data.structure.root_struct->di_file), + name, + import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), + debug_size_in_bits, + debug_align_in_bits, + ZigLLVM_DIFlags_Zero, + nullptr, di_element_types, (int)debug_field_count, 0, nullptr, ""); +} + +static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatus wanted_resolve_status) { + assert(enum_type->data.enumeration.resolve_status >= ResolveStatusSizeKnown); + if (enum_type->data.enumeration.resolve_status >= wanted_resolve_status) return; + + Scope *scope = &enum_type->data.enumeration.decls_scope->base; + ZigType *import = get_scope_import(scope); + AstNode *decl_node = enum_type->data.enumeration.decl_node; + + if (!type_has_bits(g, enum_type)) { + enum_type->llvm_type = g->builtin_types.entry_void->llvm_type; + enum_type->llvm_di_type = make_empty_namespace_llvm_di_type(g, import, buf_ptr(&enum_type->name), + decl_node); + enum_type->data.enumeration.resolve_status = ResolveStatusLLVMFull; + return; + } + + uint32_t field_count = enum_type->data.enumeration.src_field_count; + + assert(field_count == 0 || enum_type->data.enumeration.fields != nullptr); + ZigLLVMDIEnumerator **di_enumerators = heap::c_allocator.allocate(field_count); + + for (uint32_t i = 0; i < field_count; i += 1) { + TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i]; + + // TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t + // http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name), + bigint_as_signed(&enum_field->value)); + } + + ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type; + enum_type->llvm_type = get_llvm_type(g, tag_int_type); + + // create debug type for tag + uint64_t tag_debug_size_in_bits = 8*tag_int_type->abi_size; + uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align; + ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name), + import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, + tag_debug_align_in_bits, + di_enumerators, field_count, + get_llvm_di_type(g, tag_int_type), ""); + + enum_type->llvm_di_type = tag_di_type; + enum_type->data.enumeration.resolve_status = ResolveStatusLLVMFull; +} + +static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveStatus wanted_resolve_status) { + if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return; + + bool packed = (union_type->data.unionation.layout == ContainerLayoutPacked); + Scope *scope = &union_type->data.unionation.decls_scope->base; + ZigType *import = get_scope_import(scope); + + TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member; + ZigType *tag_type = union_type->data.unionation.tag_type; + uint32_t gen_field_count = union_type->data.unionation.gen_field_count; + if (gen_field_count == 0) { + if (tag_type == nullptr) { + union_type->llvm_type = g->builtin_types.entry_void->llvm_type; + union_type->llvm_di_type = make_empty_namespace_llvm_di_type(g, import, buf_ptr(&union_type->name), + union_type->data.unionation.decl_node); + } else { + union_type->llvm_type = get_llvm_type(g, tag_type); + union_type->llvm_di_type = get_llvm_di_type(g, tag_type); + } + union_type->data.unionation.resolve_status = ResolveStatusLLVMFull; + return; + } + + AstNode *decl_node = union_type->data.unionation.decl_node; + + if (union_type->data.unionation.resolve_status < ResolveStatusLLVMFwdDecl) { + union_type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&union_type->name)); + size_t line = decl_node ? decl_node->line : 0; + unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); + union_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + dwarf_kind, buf_ptr(&union_type->name), + ZigLLVMFileToScope(import->data.structure.root_struct->di_file), + import->data.structure.root_struct->di_file, (unsigned)(line + 1)); + + union_type->data.unionation.resolve_status = ResolveStatusLLVMFwdDecl; + if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return; + } + + ZigLLVMDIType **union_inner_di_types = heap::c_allocator.allocate(gen_field_count); + uint32_t field_count = union_type->data.unionation.src_field_count; + for (uint32_t i = 0; i < field_count; i += 1) { + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + if (!type_has_bits(g, union_field->type_entry)) + continue; + + ZigLLVMDIType *field_di_type = get_llvm_di_type(g, union_field->type_entry); + if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return; + + uint64_t store_size_in_bits = union_field->type_entry->size_in_bits; + uint64_t abi_align_in_bits = 8*union_field->type_entry->abi_align; + AstNode *field_node = union_field->decl_node; + union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name), + import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1), + store_size_in_bits, + abi_align_in_bits, + 0, + ZigLLVM_DIFlags_Zero, field_di_type); + + } + + if (tag_type == nullptr || !type_has_bits(g, tag_type)) { + assert(most_aligned_union_member != nullptr); + + size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size; + if (padding_bytes > 0) { + ZigType *u8_type = get_int_type(g, false, 8); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr); + LLVMTypeRef union_element_types[] = { + most_aligned_union_member->type_entry->llvm_type, + get_llvm_type(g, padding_array), + }; + LLVMStructSetBody(union_type->llvm_type, union_element_types, 2, packed); + } else { + LLVMStructSetBody(union_type->llvm_type, &most_aligned_union_member->type_entry->llvm_type, 1, packed); + } + union_type->data.unionation.union_llvm_type = union_type->llvm_type; + union_type->data.unionation.gen_tag_index = SIZE_MAX; + union_type->data.unionation.gen_union_index = SIZE_MAX; + + // create debug type for union + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, + ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&union_type->name), + import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), + union_type->data.unionation.union_abi_size * 8, + most_aligned_union_member->align * 8, + ZigLLVM_DIFlags_Zero, union_inner_di_types, + gen_field_count, 0, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type); + union_type->llvm_di_type = replacement_di_type; + union_type->data.unionation.resolve_status = ResolveStatusLLVMFull; + return; + } + + LLVMTypeRef union_type_ref; + size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size; + if (padding_bytes == 0) { + union_type_ref = get_llvm_type(g, most_aligned_union_member->type_entry); + } else { + ZigType *u8_type = get_int_type(g, false, 8); + ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr); + LLVMTypeRef union_element_types[] = { + get_llvm_type(g, most_aligned_union_member->type_entry), + get_llvm_type(g, padding_array), + }; + union_type_ref = LLVMStructType(union_element_types, 2, false); + } + union_type->data.unionation.union_llvm_type = union_type_ref; + + LLVMTypeRef root_struct_element_types[2]; + root_struct_element_types[union_type->data.unionation.gen_tag_index] = get_llvm_type(g, tag_type); + root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref; + LLVMStructSetBody(union_type->llvm_type, root_struct_element_types, 2, packed); + + // create debug type for union + ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, + ZigLLVMTypeToScope(union_type->llvm_di_type), "AnonUnion", + import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), + most_aligned_union_member->type_entry->size_in_bits, 8*most_aligned_union_member->align, + ZigLLVM_DIFlags_Zero, union_inner_di_types, gen_field_count, 0, ""); + + uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type, + union_type->data.unionation.gen_union_index); + uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type, + union_type->data.unionation.gen_tag_index); + + ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(union_type->llvm_di_type), "payload", + import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), + most_aligned_union_member->type_entry->size_in_bits, + 8*most_aligned_union_member->align, + union_offset_in_bits, + ZigLLVM_DIFlags_Zero, union_di_type); + + uint64_t tag_debug_size_in_bits = tag_type->size_in_bits; + uint64_t tag_debug_align_in_bits = 8*tag_type->abi_align; + + ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(union_type->llvm_di_type), "tag", + import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, + tag_debug_align_in_bits, + tag_offset_in_bits, + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, tag_type)); + + ZigLLVMDIType *di_root_members[2]; + di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type; + di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type; + + uint64_t debug_size_in_bits = union_type->size_in_bits; + uint64_t debug_align_in_bits = 8*union_type->abi_align; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + ZigLLVMFileToScope(import->data.structure.root_struct->di_file), + buf_ptr(&union_type->name), + import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), + debug_size_in_bits, + debug_align_in_bits, + ZigLLVM_DIFlags_Zero, nullptr, di_root_members, 2, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type); + union_type->llvm_di_type = replacement_di_type; + union_type->data.unionation.resolve_status = ResolveStatusLLVMFull; +} + +static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { + if (type->llvm_di_type != nullptr) return; + + if (resolve_pointer_zero_bits(g, type) != ErrorNone) + zig_unreachable(); + + if (!type_has_bits(g, type)) { + type->llvm_type = g->builtin_types.entry_void->llvm_type; + type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type; + return; + } + + ZigType *elem_type = type->data.pointer.child_type; + + if (type->data.pointer.is_const || type->data.pointer.is_volatile || + type->data.pointer.explicit_alignment != 0 || type->data.pointer.ptr_len != PtrLenSingle || + type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero || + type->data.pointer.vector_index != VECTOR_INDEX_NONE || type->data.pointer.sentinel != nullptr) + { + assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl)); + ZigType *peer_type; + if (type->data.pointer.vector_index == VECTOR_INDEX_NONE) { + peer_type = get_pointer_to_type_extra2(g, elem_type, false, false, + PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false, + VECTOR_INDEX_NONE, nullptr, nullptr); + } else { + uint32_t host_vec_len = type->data.pointer.host_int_bytes; + ZigType *host_vec_type = get_vector_type(g, host_vec_len, elem_type); + peer_type = get_pointer_to_type_extra2(g, host_vec_type, false, false, + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr); + } + type->llvm_type = get_llvm_type(g, peer_type); + type->llvm_di_type = get_llvm_di_type(g, peer_type); + assertNoError(type_resolve(g, elem_type, wanted_resolve_status)); + return; + } + + if (type->data.pointer.host_int_bytes == 0) { + assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl)); + type->llvm_type = LLVMPointerType(elem_type->llvm_type, 0); + uint64_t debug_size_in_bits = 8*get_store_size_bytes(type->size_in_bits); + uint64_t debug_align_in_bits = 8*type->abi_align; + type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, elem_type->llvm_di_type, + debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name)); + assertNoError(type_resolve(g, elem_type, wanted_resolve_status)); + } else { + ZigType *host_int_type = get_int_type(g, false, type->data.pointer.host_int_bytes * 8); + LLVMTypeRef host_int_llvm_type = get_llvm_type(g, host_int_type); + type->llvm_type = LLVMPointerType(host_int_llvm_type, 0); + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, host_int_llvm_type); + uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, host_int_llvm_type); + type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, host_int_type), + debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name)); + } +} + +static void resolve_llvm_types_integer(CodeGen *g, ZigType *type) { + if (type->llvm_di_type != nullptr) return; + + if (!type_has_bits(g, type)) { + type->llvm_type = g->builtin_types.entry_void->llvm_type; + type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type; + return; + } + + unsigned dwarf_tag; + if (type->data.integral.is_signed) { + if (type->size_in_bits == 8) { + dwarf_tag = ZigLLVMEncoding_DW_ATE_signed_char(); + } else { + dwarf_tag = ZigLLVMEncoding_DW_ATE_signed(); + } + } else { + if (type->size_in_bits == 8) { + dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned_char(); + } else { + dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned(); + } + } + + type->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&type->name), + type->abi_size * 8, dwarf_tag); + type->llvm_type = LLVMIntType(type->size_in_bits); +} + +static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { + assert(type->id == ZigTypeIdOptional); + assert(type->data.maybe.resolve_status != ResolveStatusInvalid); + assert(type->data.maybe.resolve_status >= ResolveStatusSizeKnown); + if (type->data.maybe.resolve_status >= wanted_resolve_status) return; + + LLVMTypeRef bool_llvm_type = get_llvm_type(g, g->builtin_types.entry_bool); + ZigLLVMDIType *bool_llvm_di_type = get_llvm_di_type(g, g->builtin_types.entry_bool); + + ZigType *child_type = type->data.maybe.child_type; + if (!type_has_bits(g, child_type)) { + type->llvm_type = bool_llvm_type; + type->llvm_di_type = bool_llvm_di_type; + type->data.maybe.resolve_status = ResolveStatusLLVMFull; + return; + } + + if (type_is_nonnull_ptr(g, child_type) || child_type->id == ZigTypeIdErrorSet) { + type->llvm_type = get_llvm_type(g, child_type); + type->llvm_di_type = get_llvm_di_type(g, child_type); + type->data.maybe.resolve_status = ResolveStatusLLVMFull; + return; + } + + ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); + ZigLLVMDIFile *di_file = nullptr; + unsigned line = 0; + + if (type->data.maybe.resolve_status < ResolveStatusLLVMFwdDecl) { + type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name)); + unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); + type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + dwarf_kind, buf_ptr(&type->name), + compile_unit_scope, di_file, line); + + type->data.maybe.resolve_status = ResolveStatusLLVMFwdDecl; + if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return; + } + + ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type); + if (type->data.maybe.resolve_status >= wanted_resolve_status) return; + + LLVMTypeRef elem_types[] = { + get_llvm_type(g, child_type), + LLVMInt1Type(), + }; + LLVMStructSetBody(type->llvm_type, elem_types, 2, false); + + uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_child_index); + uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_null_index); + + ZigLLVMDIType *di_element_types[2]; + di_element_types[maybe_child_index] = + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), + "val", di_file, line, + 8 * child_type->abi_size, + 8 * child_type->abi_align, + val_offset_in_bits, + ZigLLVM_DIFlags_Zero, child_llvm_di_type); + di_element_types[maybe_null_index] = + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type), + "maybe", di_file, line, + 8*g->builtin_types.entry_bool->abi_size, + 8*g->builtin_types.entry_bool->abi_align, + maybe_offset_in_bits, + ZigLLVM_DIFlags_Zero, bool_llvm_di_type); + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&type->name), + di_file, line, 8 * type->abi_size, 8 * type->abi_align, ZigLLVM_DIFlags_Zero, + nullptr, di_element_types, 2, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type); + type->llvm_di_type = replacement_di_type; + type->data.maybe.resolve_status = ResolveStatusLLVMFull; +} + +static void resolve_llvm_types_error_union(CodeGen *g, ZigType *type) { + if (type->llvm_di_type != nullptr) return; + + ZigType *payload_type = type->data.error_union.payload_type; + ZigType *err_set_type = type->data.error_union.err_set_type; + + if (!type_has_bits(g, payload_type)) { + assert(type_has_bits(g, err_set_type)); + type->llvm_type = get_llvm_type(g, err_set_type); + type->llvm_di_type = get_llvm_di_type(g, err_set_type); + } else if (!type_has_bits(g, err_set_type)) { + type->llvm_type = get_llvm_type(g, payload_type); + type->llvm_di_type = get_llvm_di_type(g, payload_type); + } else { + LLVMTypeRef err_set_llvm_type = get_llvm_type(g, err_set_type); + LLVMTypeRef payload_llvm_type = get_llvm_type(g, payload_type); + LLVMTypeRef elem_types[3]; + elem_types[err_union_err_index] = err_set_llvm_type; + elem_types[err_union_payload_index] = payload_llvm_type; + + type->llvm_type = LLVMStructType(elem_types, 2, false); + if (LLVMABISizeOfType(g->target_data_ref, type->llvm_type) != type->abi_size) { + // we need to do our own padding + type->data.error_union.pad_llvm_type = LLVMArrayType(LLVMInt8Type(), type->data.error_union.pad_bytes); + elem_types[2] = type->data.error_union.pad_llvm_type; + type->llvm_type = LLVMStructType(elem_types, 3, false); + } + + ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); + ZigLLVMDIFile *di_file = nullptr; + unsigned line = 0; + type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name), + compile_unit_scope, di_file, line); + + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_llvm_type); + uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_llvm_type); + uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, err_union_err_index); + + uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_llvm_type); + uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_llvm_type); + uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, + err_union_payload_index); + + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type); + uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type); + + ZigLLVMDIType *di_element_types[2]; + di_element_types[err_union_err_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(type->llvm_di_type), + "tag", di_file, line, + tag_debug_size_in_bits, + tag_debug_align_in_bits, + tag_offset_in_bits, + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, err_set_type)); + di_element_types[err_union_payload_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(type->llvm_di_type), + "value", di_file, line, + value_debug_size_in_bits, + value_debug_align_in_bits, + value_offset_in_bits, + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, payload_type)); + + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&type->name), + di_file, line, + debug_size_in_bits, + debug_align_in_bits, + ZigLLVM_DIFlags_Zero, + nullptr, di_element_types, 2, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type); + type->llvm_di_type = replacement_di_type; + } +} + +static void resolve_llvm_types_array(CodeGen *g, ZigType *type) { + if (type->llvm_di_type != nullptr) return; + + if (!type_has_bits(g, type)) { + type->llvm_type = g->builtin_types.entry_void->llvm_type; + type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type; + return; + } + + ZigType *elem_type = type->data.array.child_type; + + uint64_t extra_len_from_sentinel = (type->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = type->data.array.len + extra_len_from_sentinel; + // TODO https://github.com/ziglang/zig/issues/1424 + type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)full_len); + + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type); + uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type); + + type->llvm_di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, debug_size_in_bits, + debug_align_in_bits, get_llvm_di_type(g, elem_type), (int)full_len); +} + +static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { + if (fn_type->llvm_di_type != nullptr) return; + + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + bool first_arg_return = want_first_arg_sret(g, fn_type_id); + bool is_async = fn_type_id->cc == CallingConventionAsync; + bool is_c_abi = !calling_convention_allows_zig_types(fn_type_id->cc); + bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); + // +1 for maybe making the first argument the return value + // +1 for maybe first argument the error return trace + // +2 for maybe arguments async allocator and error code pointer + ZigList gen_param_types = {}; + // +1 because 0 is the return type and + // +1 for maybe making first arg ret val and + // +1 for maybe first argument the error return trace + // +2 for maybe arguments async allocator and error code pointer + ZigList param_di_types = {}; + ZigType *gen_return_type; + if (is_async) { + gen_return_type = g->builtin_types.entry_void; + param_di_types.append(nullptr); + } else if (!type_has_bits(g, fn_type_id->return_type)) { + gen_return_type = g->builtin_types.entry_void; + param_di_types.append(nullptr); + } else if (first_arg_return) { + gen_return_type = g->builtin_types.entry_void; + param_di_types.append(nullptr); + ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false); + gen_param_types.append(get_llvm_type(g, gen_type)); + param_di_types.append(get_llvm_di_type(g, gen_type)); + } else { + gen_return_type = fn_type_id->return_type; + param_di_types.append(get_llvm_di_type(g, gen_return_type)); + } + fn_type->data.fn.gen_return_type = gen_return_type; + + if (prefix_arg_error_return_trace && !is_async) { + ZigType *gen_type = get_pointer_to_type(g, get_stack_trace_type(g), false); + gen_param_types.append(get_llvm_type(g, gen_type)); + param_di_types.append(get_llvm_di_type(g, gen_type)); + } + if (is_async) { + fn_type->data.fn.gen_param_info = heap::c_allocator.allocate(2); + + ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type); + gen_param_types.append(get_llvm_type(g, frame_type)); + param_di_types.append(get_llvm_di_type(g, frame_type)); + + fn_type->data.fn.gen_param_info[0].src_index = 0; + fn_type->data.fn.gen_param_info[0].gen_index = 0; + fn_type->data.fn.gen_param_info[0].type = frame_type; + + gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize)); + param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize)); + + fn_type->data.fn.gen_param_info[1].src_index = 1; + fn_type->data.fn.gen_param_info[1].gen_index = 1; + fn_type->data.fn.gen_param_info[1].type = g->builtin_types.entry_usize; + } else { + fn_type->data.fn.gen_param_info = heap::c_allocator.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]; + ZigType *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; + + if (is_c_abi || !type_has_bits(g, type_entry)) + continue; + + ZigType *gen_type; + if (handle_is_ptr(g, 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_info->gen_index = gen_param_types.length; + gen_param_info->type = gen_type; + gen_param_types.append(get_llvm_type(g, gen_type)); + + param_di_types.append(get_llvm_di_type(g, gen_type)); + } + } + + if (is_c_abi) { + FnWalk fn_walk = {}; + fn_walk.id = FnWalkIdTypes; + fn_walk.data.types.param_di_types = ¶m_di_types; + fn_walk.data.types.gen_param_types = &gen_param_types; + walk_function_params(g, fn_type, &fn_walk); + } + + fn_type->data.fn.gen_param_count = gen_param_types.length; + + for (size_t i = 0; i < gen_param_types.length; i += 1) { + assert(gen_param_types.items[i] != nullptr); + } + + fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), + gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args); + const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); + fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, fn_addrspace); + fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); + fn_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, fn_type->data.fn.raw_di_type, + LLVMStoreSizeOfType(g->target_data_ref, fn_type->llvm_type), + LLVMABIAlignmentOfType(g->target_data_ref, fn_type->llvm_type), ""); + + gen_param_types.deinit(); + param_di_types.deinit(); +} + +void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) { + Error err; + if (fn->raw_di_type != nullptr) return; + + ZigType *fn_type = fn->type_entry; + if (!fn_is_async(fn)) { + resolve_llvm_types_fn_type(g, fn_type); + fn->raw_type_ref = fn_type->data.fn.raw_type_ref; + fn->raw_di_type = fn_type->data.fn.raw_di_type; + return; + } + + ZigType *gen_return_type = g->builtin_types.entry_void; + ZigList param_di_types = {}; + ZigList gen_param_types = {}; + // first "parameter" is return value + param_di_types.append(nullptr); + + ZigType *frame_type = get_fn_frame_type(g, fn); + ZigType *ptr_type = get_pointer_to_type(g, frame_type, false); + if ((err = type_resolve(g, ptr_type, ResolveStatusLLVMFwdDecl))) + zig_unreachable(); + gen_param_types.append(ptr_type->llvm_type); + param_di_types.append(ptr_type->llvm_di_type); + + // this parameter is used to pass the result pointer when await completes + gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize)); + param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize)); + + fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), + gen_param_types.items, gen_param_types.length, false); + fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); + + param_di_types.deinit(); + gen_param_types.deinit(); +} + +static void resolve_llvm_types_anyerror(CodeGen *g) { + ZigType *entry = g->builtin_types.entry_global_error_set; + entry->llvm_type = get_llvm_type(g, g->err_tag_type); + ZigList err_enumerators = {}; + // reserve index 0 to indicate no error + err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, "(none)", 0)); + for (size_t i = 1; i < g->errors_by_index.length; i += 1) { + ErrorTableEntry *error_entry = g->errors_by_index.at(i); + err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(&error_entry->name), i)); + } + + // create debug type for error sets + uint64_t tag_debug_size_in_bits = g->err_tag_type->size_in_bits; + uint64_t tag_debug_align_in_bits = 8*g->err_tag_type->abi_align; + ZigLLVMDIFile *err_set_di_file = nullptr; + entry->llvm_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMCompileUnitToScope(g->compile_unit), buf_ptr(&entry->name), + err_set_di_file, 0, + tag_debug_size_in_bits, + tag_debug_align_in_bits, + err_enumerators.items, err_enumerators.length, + get_llvm_di_type(g, g->err_tag_type), ""); + + err_enumerators.deinit(); +} + +static void resolve_llvm_types_async_frame(CodeGen *g, ZigType *frame_type, ResolveStatus wanted_resolve_status) { + Error err; + if ((err = type_resolve(g, frame_type, ResolveStatusSizeKnown))) + zig_unreachable(); + + ZigType *passed_frame_type = fn_is_async(frame_type->data.frame.fn) ? frame_type : nullptr; + resolve_llvm_types_struct(g, frame_type->data.frame.locals_struct, wanted_resolve_status, passed_frame_type); + frame_type->llvm_type = frame_type->data.frame.locals_struct->llvm_type; + frame_type->llvm_di_type = frame_type->data.frame.locals_struct->llvm_di_type; +} + +static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, ResolveStatus wanted_resolve_status) { + if (any_frame_type->llvm_di_type != nullptr) return; + + Buf *name = buf_sprintf("(%s header)", buf_ptr(&any_frame_type->name)); + LLVMTypeRef frame_header_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name)); + any_frame_type->llvm_type = LLVMPointerType(frame_header_type, 0); + + unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); + ZigLLVMDIFile *di_file = nullptr; + ZigLLVMDIScope *di_scope = ZigLLVMCompileUnitToScope(g->compile_unit); + unsigned line = 0; + ZigLLVMDIType *frame_header_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + dwarf_kind, buf_ptr(name), di_scope, di_file, line); + any_frame_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, frame_header_di_type, + 8*g->pointer_size_bytes, 8*g->builtin_types.entry_usize->abi_align, buf_ptr(&any_frame_type->name)); + + LLVMTypeRef llvm_void = LLVMVoidType(); + LLVMTypeRef arg_types[] = {any_frame_type->llvm_type, g->builtin_types.entry_usize->llvm_type}; + LLVMTypeRef fn_type = LLVMFunctionType(llvm_void, arg_types, 2, false); + LLVMTypeRef usize_type_ref = get_llvm_type(g, g->builtin_types.entry_usize); + ZigLLVMDIType *usize_di_type = get_llvm_di_type(g, g->builtin_types.entry_usize); + ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); + + ZigType *result_type = any_frame_type->data.any_frame.result_type; + ZigType *ptr_result_type = (result_type == nullptr) ? nullptr : get_pointer_to_type(g, result_type, false); + const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); + LLVMTypeRef ptr_fn_llvm_type = LLVMPointerType(fn_type, fn_addrspace); + if (result_type == nullptr) { + g->anyframe_fn_type = ptr_fn_llvm_type; + } + + ZigList field_types = {}; + ZigList di_element_types = {}; + + // label (grep this): [fn_frame_struct_layout] + field_types.append(ptr_fn_llvm_type); // fn_ptr + field_types.append(usize_type_ref); // resume_index + field_types.append(usize_type_ref); // awaiter + + bool have_result_type = result_type != nullptr && type_has_bits(g, result_type); + if (have_result_type) { + field_types.append(get_llvm_type(g, ptr_result_type)); // result_ptr_callee + field_types.append(get_llvm_type(g, ptr_result_type)); // result_ptr_awaiter + field_types.append(get_llvm_type(g, result_type)); // result + if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) { + ZigType *ptr_stack_trace = get_pointer_to_type(g, get_stack_trace_type(g), false); + field_types.append(get_llvm_type(g, ptr_stack_trace)); // ptr_stack_trace_callee + field_types.append(get_llvm_type(g, ptr_stack_trace)); // ptr_stack_trace_awaiter + } + } + LLVMStructSetBody(frame_header_type, field_types.items, field_types.length, false); + + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "fn_ptr", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, usize_di_type)); + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "resume_index", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, usize_di_type)); + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "awaiter", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, usize_di_type)); + + if (have_result_type) { + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr_callee", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type))); + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr_awaiter", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type))); + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, result_type))); + + if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) { + ZigType *ptr_stack_trace = get_pointer_to_type(g, get_stack_trace_type(g), false); + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_stack_trace_callee", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_stack_trace))); + di_element_types.append( + ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_stack_trace_awaiter", + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)), + 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length), + ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_stack_trace))); + } + }; + + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + compile_unit_scope, buf_ptr(name), + di_file, line, + 8*LLVMABISizeOfType(g->target_data_ref, frame_header_type), + 8*LLVMABIAlignmentOfType(g->target_data_ref, frame_header_type), + ZigLLVM_DIFlags_Zero, + nullptr, di_element_types.items, di_element_types.length, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type); + + field_types.deinit(); + di_element_types.deinit(); +} + +static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { + assert(wanted_resolve_status > ResolveStatusSizeKnown); + switch (type->id) { + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + zig_unreachable(); + case ZigTypeIdFloat: + case ZigTypeIdOpaque: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + assert(type->llvm_di_type != nullptr); + return; + case ZigTypeIdStruct: + if (type->data.structure.special == StructSpecialSlice) + return resolve_llvm_types_slice(g, type, wanted_resolve_status); + else + return resolve_llvm_types_struct(g, type, wanted_resolve_status, nullptr); + case ZigTypeIdEnum: + return resolve_llvm_types_enum(g, type, wanted_resolve_status); + case ZigTypeIdUnion: + return resolve_llvm_types_union(g, type, wanted_resolve_status); + case ZigTypeIdPointer: + return resolve_llvm_types_pointer(g, type, wanted_resolve_status); + case ZigTypeIdInt: + return resolve_llvm_types_integer(g, type); + case ZigTypeIdOptional: + return resolve_llvm_types_optional(g, type, wanted_resolve_status); + case ZigTypeIdErrorUnion: + return resolve_llvm_types_error_union(g, type); + case ZigTypeIdArray: + return resolve_llvm_types_array(g, type); + case ZigTypeIdFn: + return resolve_llvm_types_fn_type(g, type); + case ZigTypeIdErrorSet: { + if (type->llvm_di_type != nullptr) return; + + if (g->builtin_types.entry_global_error_set->llvm_type == nullptr) { + resolve_llvm_types_anyerror(g); + } + type->llvm_type = g->builtin_types.entry_global_error_set->llvm_type; + type->llvm_di_type = g->builtin_types.entry_global_error_set->llvm_di_type; + return; + } + case ZigTypeIdVector: { + if (type->llvm_di_type != nullptr) return; + + type->llvm_type = LLVMVectorType(get_llvm_type(g, type->data.vector.elem_type), type->data.vector.len); + type->llvm_di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, 8 * type->abi_size, + type->abi_align, get_llvm_di_type(g, type->data.vector.elem_type), type->data.vector.len); + return; + } + case ZigTypeIdFnFrame: + return resolve_llvm_types_async_frame(g, type, wanted_resolve_status); + case ZigTypeIdAnyFrame: + return resolve_llvm_types_any_frame(g, type, wanted_resolve_status); + } + zig_unreachable(); +} + +LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type) { + assertNoError(type_resolve(g, type, ResolveStatusLLVMFull)); + assert(type->abi_size == 0 || type->abi_size >= LLVMABISizeOfType(g->target_data_ref, type->llvm_type)); + assert(type->abi_align == 0 || type->abi_align >= LLVMABIAlignmentOfType(g->target_data_ref, type->llvm_type)); + return type->llvm_type; +} + +ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type) { + assertNoError(type_resolve(g, type, ResolveStatusLLVMFull)); + return type->llvm_di_type; +} + +void src_assert_impl(bool ok, AstNode *source_node, char const *file, unsigned int line) { + if (ok) return; + if (source_node == nullptr) { + fprintf(stderr, "when analyzing (unknown source location) "); + } else { + fprintf(stderr, "when analyzing %s:%u:%u ", + buf_ptr(source_node->owner->data.structure.root_struct->path), + (unsigned)source_node->line + 1, (unsigned)source_node->column + 1); + } + fprintf(stderr, "in compiler source at %s:%u: ", file, line); + const char *msg = "assertion failed. This is a bug in the Zig compiler."; + stage2_panic(msg, strlen(msg)); +} + +Error analyze_import(CodeGen *g, ZigType *source_import, Buf *import_target_str, + ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path) +{ + Error err; + + Buf *search_dir; + ZigPackage *cur_scope_pkg = source_import->data.structure.root_struct->package; + assert(cur_scope_pkg); + ZigPackage *target_package; + auto package_entry = cur_scope_pkg->package_table.maybe_get(import_target_str); + SourceKind source_kind; + if (package_entry) { + target_package = package_entry->value; + *out_import_target_path = &target_package->root_src_path; + search_dir = &target_package->root_src_dir; + source_kind = SourceKindPkgMain; + } else { + // try it as a filename + target_package = cur_scope_pkg; + *out_import_target_path = import_target_str; + + // search relative to importing file + search_dir = buf_alloc(); + os_path_dirname(source_import->data.structure.root_struct->path, search_dir); + + source_kind = SourceKindNonRoot; + } + + buf_resize(out_full_path, 0); + os_path_join(search_dir, *out_import_target_path, out_full_path); + + Buf *import_code = buf_alloc(); + Buf *resolved_path = buf_alloc(); + + Buf *resolve_paths[] = { out_full_path, }; + *resolved_path = os_path_resolve(resolve_paths, 1); + + auto import_entry = g->import_table.maybe_get(resolved_path); + if (import_entry) { + *out_import = import_entry->value; + return ErrorNone; + } + + if (source_kind == SourceKindNonRoot) { + Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir; + Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1); + if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) { + return ErrorImportOutsidePkgPath; + } + } + + if ((err = file_fetch(g, resolved_path, import_code))) { + return err; + } + + *out_import = add_source_file(g, target_package, resolved_path, import_code, source_kind); + return ErrorNone; +} + + +void IrExecutableSrc::src() { + if (this->source_node != nullptr) { + this->source_node->src(); + } + if (this->parent_exec != nullptr) { + this->parent_exec->src(); + } +} + +void IrExecutableGen::src() { + IrExecutableGen *it; + for (it = this; it != nullptr && it->source_node != nullptr; it = it->parent_exec) { + it->source_node->src(); + } +} + +bool is_anon_container(ZigType *ty) { + return ty->id == ZigTypeIdStruct && ( + ty->data.structure.special == StructSpecialInferredTuple || + ty->data.structure.special == StructSpecialInferredStruct); +} + +bool is_opt_err_set(ZigType *ty) { + return ty->id == ZigTypeIdErrorSet || + (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); +} + +// Returns whether the x_optional field of ZigValue is active. +bool type_has_optional_repr(ZigType *ty) { + if (ty->id != ZigTypeIdOptional) { + return false; + } else if (get_src_ptr_type(ty) != nullptr) { + return false; + } else if (is_opt_err_set(ty)) { + return false; + } else { + return true; + } +} + +void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) { + uint32_t prev_align = dest->llvm_align; + ConstParent prev_parent = dest->parent; + memcpy(dest, src, sizeof(ZigValue)); + dest->llvm_align = prev_align; + if (src->special != ConstValSpecialStatic) + return; + dest->parent = prev_parent; + if (dest->type->id == ZigTypeIdStruct) { + dest->data.x_struct.fields = alloc_const_vals_ptrs(g, dest->type->data.structure.src_field_count); + for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) { + copy_const_val(g, dest->data.x_struct.fields[i], src->data.x_struct.fields[i]); + dest->data.x_struct.fields[i]->parent.id = ConstParentIdStruct; + dest->data.x_struct.fields[i]->parent.data.p_struct.struct_val = dest; + dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i; + } + } else if (dest->type->id == ZigTypeIdArray) { + switch (dest->data.x_array.special) { + case ConstArraySpecialNone: { + dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len); + for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) { + copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]); + dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest; + dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i; + } + break; + } + case ConstArraySpecialUndef: { + // Nothing to copy; the above memcpy did everything we needed. + break; + } + case ConstArraySpecialBuf: { + dest->data.x_array.data.s_buf = buf_create_from_buf(src->data.x_array.data.s_buf); + break; + } + } + } else if (dest->type->id == ZigTypeIdUnion) { + bigint_init_bigint(&dest->data.x_union.tag, &src->data.x_union.tag); + dest->data.x_union.payload = g->pass1_arena->create(); + copy_const_val(g, dest->data.x_union.payload, src->data.x_union.payload); + dest->data.x_union.payload->parent.id = ConstParentIdUnion; + dest->data.x_union.payload->parent.data.p_union.union_val = dest; + } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) { + dest->data.x_optional = g->pass1_arena->create(); + copy_const_val(g, dest->data.x_optional, src->data.x_optional); + dest->data.x_optional->parent.id = ConstParentIdOptionalPayload; + dest->data.x_optional->parent.data.p_optional_payload.optional_val = dest; + } +} + +bool optional_value_is_null(ZigValue *val) { + assert(val->special == ConstValSpecialStatic); + if (get_src_ptr_type(val->type) != nullptr) { + if (val->data.x_ptr.special == ConstPtrSpecialNull) { + return true; + } else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + return val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else { + return false; + } + } else if (is_opt_err_set(val->type)) { + return val->data.x_err_set == nullptr; + } else { + return val->data.x_optional == nullptr; + } +} + +bool type_is_numeric(ZigType *ty) { + switch (ty->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdUndefined: + return true; + + case ZigTypeIdVector: + return type_is_numeric(ty->data.vector.elem_type); + + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + case ZigTypeIdEnumLiteral: + return false; + } + zig_unreachable(); +} + +static void dump_value_indent_error_set(ZigValue *val, int indent) { + fprintf(stderr, "\n"); +} + +static void dump_value_indent(ZigValue *val, int indent); + +static void dump_value_indent_ptr(ZigValue *val, int indent) { + switch (val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + fprintf(stderr, "\n"); + return; + case ConstPtrSpecialNull: + fprintf(stderr, "\n"); + return; + case ConstPtrSpecialRef: + fprintf(stderr, "data.x_ptr.data.ref.pointee, indent + 1); + break; + case ConstPtrSpecialBaseStruct: { + ZigValue *struct_val = val->data.x_ptr.data.base_struct.struct_val; + size_t field_index = val->data.x_ptr.data.base_struct.field_index; + fprintf(stderr, "data.x_struct.fields[field_index]; + if (field_val != nullptr) { + dump_value_indent(field_val, indent + 1); + } else { + for (int i = 0; i < indent; i += 1) { + fprintf(stderr, " "); + } + fprintf(stderr, "(invalid null field)\n"); + } + } + break; + } + case ConstPtrSpecialBaseOptionalPayload: { + ZigValue *optional_val = val->data.x_ptr.data.base_optional_payload.optional_val; + fprintf(stderr, "\n"); +} + +static void dump_value_indent(ZigValue *val, int indent) { + for (int i = 0; i < indent; i += 1) { + fprintf(stderr, " "); + } + fprintf(stderr, "Value@%p(", val); + if (val->type != nullptr) { + fprintf(stderr, "%s)", buf_ptr(&val->type->name)); + } else { + fprintf(stderr, "type=nullptr)"); + } + switch (val->special) { + case ConstValSpecialUndef: + fprintf(stderr, "[undefined]\n"); + return; + case ConstValSpecialLazy: + fprintf(stderr, "[lazy]\n"); + return; + case ConstValSpecialRuntime: + fprintf(stderr, "[runtime]\n"); + return; + case ConstValSpecialStatic: + break; + } + if (val->type == nullptr) + return; + switch (val->type->id) { + case ZigTypeIdInvalid: + fprintf(stderr, "\n"); + return; + case ZigTypeIdUnreachable: + fprintf(stderr, "\n"); + return; + case ZigTypeIdUndefined: + fprintf(stderr, "\n"); + return; + case ZigTypeIdVoid: + fprintf(stderr, "<{}>\n"); + return; + case ZigTypeIdMetaType: + fprintf(stderr, "<%s>\n", buf_ptr(&val->data.x_type->name)); + return; + case ZigTypeIdBool: + fprintf(stderr, "<%s>\n", val->data.x_bool ? "true" : "false"); + return; + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: { + Buf *tmp_buf = buf_alloc(); + bigint_append_buf(tmp_buf, &val->data.x_bigint, 10); + fprintf(stderr, "<%s>\n", buf_ptr(tmp_buf)); + buf_destroy(tmp_buf); + return; + } + case ZigTypeIdComptimeFloat: + case ZigTypeIdFloat: + fprintf(stderr, "\n"); + return; + + case ZigTypeIdStruct: + fprintf(stderr, "type->data.structure.src_field_count; i += 1) { + for (int j = 0; j < indent; j += 1) { + fprintf(stderr, " "); + } + fprintf(stderr, "%s: ", buf_ptr(val->type->data.structure.fields[i]->name)); + if (val->data.x_struct.fields == nullptr) { + fprintf(stderr, "\n"); + } else { + dump_value_indent(val->data.x_struct.fields[i], 1); + } + } + for (int i = 0; i < indent; i += 1) { + fprintf(stderr, " "); + } + fprintf(stderr, ">\n"); + return; + + case ZigTypeIdOptional: + if (get_src_ptr_type(val->type) != nullptr) { + return dump_value_indent_ptr(val, indent); + } else if (val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) { + return dump_value_indent_error_set(val, indent); + } else { + fprintf(stderr, "<\n"); + dump_value_indent(val->data.x_optional, indent + 1); + + for (int i = 0; i < indent; i += 1) { + fprintf(stderr, " "); + } + fprintf(stderr, ">\n"); + return; + } + case ZigTypeIdErrorUnion: + if (val->data.x_err_union.payload != nullptr) { + fprintf(stderr, "<\n"); + dump_value_indent(val->data.x_err_union.payload, indent + 1); + } else { + fprintf(stderr, "<\n"); + dump_value_indent(val->data.x_err_union.error_set, 0); + } + for (int i = 0; i < indent; i += 1) { + fprintf(stderr, " "); + } + fprintf(stderr, ">\n"); + return; + + case ZigTypeIdPointer: + return dump_value_indent_ptr(val, indent); + + case ZigTypeIdErrorSet: + return dump_value_indent_error_set(val, indent); + + case ZigTypeIdVector: + case ZigTypeIdArray: + case ZigTypeIdNull: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + case ZigTypeIdEnumLiteral: + fprintf(stderr, "\n"); + return; + } + zig_unreachable(); +} + +void ZigValue::dump() { + dump_value_indent(this, 0); +} + +// float ops that take a single argument +//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, lround, llround, lrint, llrint +const char *float_op_to_name(BuiltinFnId op) { + switch (op) { + case BuiltinFnIdSqrt: + return "sqrt"; + case BuiltinFnIdSin: + return "sin"; + case BuiltinFnIdCos: + return "cos"; + case BuiltinFnIdExp: + return "exp"; + case BuiltinFnIdExp2: + return "exp2"; + case BuiltinFnIdLog: + return "log"; + case BuiltinFnIdLog10: + return "log10"; + case BuiltinFnIdLog2: + return "log2"; + case BuiltinFnIdFabs: + return "fabs"; + case BuiltinFnIdFloor: + return "floor"; + case BuiltinFnIdCeil: + return "ceil"; + case BuiltinFnIdTrunc: + return "trunc"; + case BuiltinFnIdNearbyInt: + return "nearbyint"; + case BuiltinFnIdRound: + return "round"; + default: + zig_unreachable(); + } +} + -- cgit v1.2.3 From 91a73a177bc20fa0219dbb6c3cf3dda1c2a465db Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Sep 2020 00:06:06 -0700 Subject: stage2: building mingw-w64 and COFF LDD linking still TODO is the task of creating import .lib files for DLLs on the fly both for -lfoo and for e.g. `extern "kernel32"` --- BRANCH_TODO | 10 +- lib/std/zig.zig | 2 +- src/Cache.zig | 8 + src/Compilation.zig | 68 +++- src/Module.zig | 9 +- src/link.zig | 55 +-- src/link/Coff.zig | 576 ++++++++++++++++++++++++++----- src/link/Elf.zig | 25 +- src/mingw.zig | 866 +++++++++++++++++++++++++++++++++++++++++++++++ src/musl.zig | 3 - src/stage1.zig | 8 + src/stage1/all_types.hpp | 6 - src/stage1/analyze.cpp | 12 +- src/stage1/codegen.cpp | 6 +- src/stage1/stage1.h | 8 + 15 files changed, 1506 insertions(+), 156 deletions(-) create mode 100644 src/mingw.zig (limited to 'src/stage1/analyze.cpp') diff --git a/BRANCH_TODO b/BRANCH_TODO index 0bd4766207..6853b031d7 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -1,14 +1,13 @@ - * COFF LLD linking - * mingw-w64 + * add jobs to build import libs for windows DLLs for explicitly linked libs + * add jobs to build import libs for windows DLLs for extern "foo" functions used * MachO LLD linking * WASM LLD linking * audit the CLI options for stage2 * audit the base cache hash * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process. - * windows CUSTOMBUILD : error : unable to build compiler_rt: FileNotFound [D:\a\1\s\build\zig_install_lib_files.vcxproj] - * try building some software with zig cc to make sure it didn't regress * `-ftime-report` * -fstack-report print stack size diagnostics\n" + * try building some software with zig cc to make sure it didn't regress * implement proper parsing of clang stderr/stdout and exposing compile errors with the Compilation API * implement proper parsing of LLD stderr/stdout and exposing compile errors with the Compilation API @@ -35,6 +34,7 @@ * integrate target features into building assembly code * libc_installation.zig: make it look for msvc only if msvc abi is chosen * switch the default C ABI for windows to be mingw-w64 + - make it .obj instead of .o always for coff * change glibc log errors to normal exposed compile errors * improve Directory.join to only use 1 allocation in a clean way. * tracy builds with lc++ @@ -48,3 +48,5 @@ * linking hello world with LLD, lld is silently calling exit(1) instead of reporting ok=false. when run standalone the error message is: ld.lld: error: section [index 3] has a sh_offset (0x57000) + sh_size (0x68) that is greater than the file size (0x57060) * submit PR to godbolt and update the CLI options (see changes to test/cli.zig) * make proposal about log levels + * proposal for changing fs Z/W functions to be native paths and have a way to do native path string literals + * proposal for block { break x; } diff --git a/lib/std/zig.zig b/lib/std/zig.zig index fc173ecda9..59855f62d4 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -93,7 +93,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro }; return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); }, - .Obj => return std.fmt.allocPrint(allocator, "{}.obj", .{root_name}), + .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.abi.oFileExt() }), }, .elf => switch (options.output_mode) { .Exe => return allocator.dupe(u8, root_name), diff --git a/src/Cache.zig b/src/Cache.zig index cfcbc3e76a..425c6407a3 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -76,6 +76,14 @@ pub const HashHelper = struct { for (list_of_bytes) |bytes| hh.addBytes(bytes); } + pub fn addStringSet(hh: *HashHelper, hm: std.StringArrayHashMapUnmanaged(void)) void { + const entries = hm.items(); + hh.add(entries.len); + for (entries) |entry| { + hh.addBytes(entry.key); + } + } + /// Convert the input value into bytes and record it as a dependency of the process being cached. pub fn add(hh: *HashHelper, x: anytype) void { switch (@TypeOf(x)) { diff --git a/src/Compilation.zig b/src/Compilation.zig index f641f96832..e539950aba 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -17,6 +17,7 @@ const build_options = @import("build_options"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const glibc = @import("glibc.zig"); const musl = @import("musl.zig"); +const mingw = @import("mingw.zig"); const libunwind = @import("libunwind.zig"); const libcxx = @import("libcxx.zig"); const fatal = @import("main.zig").fatal; @@ -59,7 +60,6 @@ verbose_llvm_ir: bool, verbose_cimport: bool, verbose_llvm_cpu_features: bool, disable_c_depfile: bool, -is_test: bool, time_report: bool, c_source_files: []const CSourceFile, @@ -150,8 +150,10 @@ const Job = union(enum) { glibc_crt_file: glibc.CRTFile, /// all of the glibc shared objects glibc_shared_objects, - /// one of the glibc static objects + /// one of the musl static objects musl_crt_file: musl.CRTFile, + /// one of the mingw-w64 static objects + mingw_crt_file: mingw.CRTFile, /// libunwind.a, usually needed when linking libc libunwind: void, libcxx: void, @@ -719,6 +721,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { fatal("TODO implement support for -femit-h in the self-hosted backend", .{}); } + var system_libs: std.StringArrayHashMapUnmanaged(void) = .{}; + errdefer system_libs.deinit(gpa); + try system_libs.ensureCapacity(gpa, options.system_libs.len); + for (options.system_libs) |lib_name| { + system_libs.putAssumeCapacity(lib_name, {}); + } + const bin_file = try link.File.openPath(gpa, .{ .emit = bin_file_emit, .root_name = root_name, @@ -736,7 +745,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .objects = options.link_objects, .frameworks = options.frameworks, .framework_dirs = options.framework_dirs, - .system_libs = options.system_libs, + .system_libs = system_libs, .lib_dirs = options.lib_dirs, .rpath_list = options.rpath_list, .strip = options.strip, @@ -769,6 +778,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .each_lib_rpath = options.each_lib_rpath orelse false, .disable_lld_caching = options.disable_lld_caching, .subsystem = options.subsystem, + .is_test = options.is_test, }); errdefer bin_file.destroy(); comp.* = .{ @@ -804,7 +814,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, .disable_c_depfile = options.disable_c_depfile, .owned_link_dir = owned_link_dir, - .is_test = options.is_test, .color = options.color, .time_report = options.time_report, .test_filter = options.test_filter, @@ -847,8 +856,17 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .{ .musl_crt_file = .libc_a }, }); } - if (comp.wantBuildMinGWW64FromSource()) { - @panic("TODO"); + if (comp.wantBuildMinGWFromSource()) { + const static_lib_jobs = [_]Job{ + .{ .mingw_crt_file = .mingw32_lib }, + .{ .mingw_crt_file = .msvcrt_os_lib }, + .{ .mingw_crt_file = .mingwex_lib }, + .{ .mingw_crt_file = .uuid_lib }, + }; + const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; + try comp.work_queue.ensureUnusedCapacity(static_lib_jobs.len + 1); + comp.work_queue.writeAssumeCapacity(&static_lib_jobs); + comp.work_queue.writeItemAssumeCapacity(crt_job); } if (comp.wantBuildLibUnwindFromSource()) { try comp.work_queue.writeItem(.{ .libunwind = {} }); @@ -1209,6 +1227,12 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { fatal("unable to build musl CRT file: {}", .{@errorName(err)}); }; }, + .mingw_crt_file => |crt_file| { + mingw.buildCRTFile(self, crt_file) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)}); + }; + }, .libunwind => { libunwind.buildStaticLib(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. @@ -2087,7 +2111,7 @@ fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const pub fn get_libc_crt_file(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { if (comp.wantBuildGLibCFromSource() or comp.wantBuildMuslFromSource() or - comp.wantBuildMinGWW64FromSource()) + comp.wantBuildMinGWFromSource()) { return comp.crt_files.get(basename).?.full_object_path; } @@ -2125,7 +2149,7 @@ fn wantBuildMuslFromSource(comp: Compilation) bool { return comp.wantBuildLibCFromSource() and comp.getTarget().isMusl(); } -fn wantBuildMinGWW64FromSource(comp: Compilation) bool { +fn wantBuildMinGWFromSource(comp: Compilation) bool { return comp.wantBuildLibCFromSource() and comp.getTarget().isMinGW(); } @@ -2186,7 +2210,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 , .{ @tagName(comp.bin_file.options.output_mode), @tagName(comp.bin_file.options.link_mode), - comp.is_test, + comp.bin_file.options.is_test, comp.bin_file.options.single_threaded, @tagName(target.abi), @tagName(target.cpu.arch), @@ -2214,7 +2238,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\pub const os = Os{{ \\ .tag = .{}, \\ .version_range = .{{ - , + , .{@tagName(target.os.tag)}, ); @@ -2283,7 +2307,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\ .max = {s}, \\ }}}}, \\ - , + , .{ windows.min, windows.max }, ), } @@ -2311,7 +2335,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 @tagName(comp.bin_file.options.machine_code_model), }); - if (comp.is_test) { + if (comp.bin_file.options.is_test) { try buffer.appendSlice( \\pub var test_functions: []TestFn = undefined; // overwritten later \\ @@ -2384,7 +2408,7 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR .basename = bin_basename, }; const optimize_mode: std.builtin.Mode = blk: { - if (comp.is_test) + if (comp.bin_file.options.is_test) break :blk comp.bin_file.options.optimize_mode; switch (comp.bin_file.options.optimize_mode) { .Debug, .ReleaseFast, .ReleaseSafe => break :blk .ReleaseFast, @@ -2473,7 +2497,7 @@ fn updateStage1Module(comp: *Compilation) !void { man.hash.add(target.os.getVersionRange()); man.hash.add(comp.bin_file.options.dll_export_fns); man.hash.add(comp.bin_file.options.function_sections); - man.hash.add(comp.is_test); + man.hash.add(comp.bin_file.options.is_test); man.hash.add(comp.bin_file.options.emit != null); man.hash.add(comp.emit_h != null); man.hash.add(comp.emit_asm != null); @@ -2537,7 +2561,7 @@ fn updateStage1Module(comp: *Compilation) !void { zig_lib_dir.ptr, zig_lib_dir.len, stage2_target, - comp.is_test, + comp.bin_file.options.is_test, ) orelse return error.OutOfMemory; const emit_bin_path = if (comp.bin_file.options.emit != null) blk: { @@ -2609,8 +2633,22 @@ fn updateStage1Module(comp: *Compilation) !void { .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .main_progress_node = main_progress_node, + .have_c_main = false, + .have_winmain = false, + .have_wwinmain = false, + .have_winmain_crt_startup = false, + .have_wwinmain_crt_startup = false, + .have_dllmain_crt_startup = false, }; stage1_module.build_object(); + + mod.have_c_main = stage1_module.have_c_main; + mod.have_winmain = stage1_module.have_winmain; + mod.have_wwinmain = stage1_module.have_wwinmain; + mod.have_winmain_crt_startup = stage1_module.have_winmain_crt_startup; + mod.have_wwinmain_crt_startup = stage1_module.have_wwinmain_crt_startup; + mod.have_dllmain_crt_startup = stage1_module.have_dllmain_crt_startup; + stage1_module.destroy(); const digest = man.final(); diff --git a/src/Module.zig b/src/Module.zig index ebe7cdfb1e..8f87b7a521 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -75,6 +75,13 @@ global_error_set: std.StringHashMapUnmanaged(u16) = .{}, /// previous analysis. generation: u32 = 0, +have_winmain: bool = false, +have_wwinmain: bool = false, +have_winmain_crt_startup: bool = false, +have_wwinmain_crt_startup: bool = false, +have_dllmain_crt_startup: bool = false, +have_c_main: bool = false, + pub const Export = struct { options: std.builtin.ExportOptions, /// Byte offset into the file that contains the export directive. @@ -2668,7 +2675,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst const src_info = inst.ty.intInfo(self.getTarget()); const dst_info = dest_type.intInfo(self.getTarget()); if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or - // small enough unsigned ints can get casted to large enough signed ints + // small enough unsigned ints can get casted to large enough signed ints (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits)) { const b = try self.requireRuntimeBlock(scope, inst.src); diff --git a/src/link.zig b/src/link.zig index a0372da4a1..9112bd8d1d 100644 --- a/src/link.zig +++ b/src/link.zig @@ -36,7 +36,7 @@ pub const Options = struct { root_name: []const u8, /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. module: ?*Module, - dynamic_linker: ?[]const u8 = null, + dynamic_linker: ?[]const u8, /// Used for calculating how much space to reserve for symbols in case the binary file /// does not already have a symbol table. symbol_count_hint: u64 = 32, @@ -44,53 +44,54 @@ pub const Options = struct { /// the binary file does not already have such a section. program_code_size_hint: u64 = 256 * 1024, entry_addr: ?u64 = null, - stack_size_override: ?u64 = null, + stack_size_override: ?u64, /// Set to `true` to omit debug info. - strip: bool = false, + strip: bool, /// If this is true then this link code is responsible for outputting an object /// file and then using LLD to link it together with the link options and other objects. /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary. - use_lld: bool = false, + use_lld: bool, /// If this is true then this link code is responsible for making an LLVM IR Module, /// outputting it to an object file, and then linking that together with link options and /// other objects. /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. - use_llvm: bool = false, - link_libc: bool = false, - link_libcpp: bool = false, - function_sections: bool = false, - eh_frame_hdr: bool = false, - rdynamic: bool = false, - z_nodelete: bool = false, - z_defs: bool = false, + use_llvm: bool, + link_libc: bool, + link_libcpp: bool, + function_sections: bool, + eh_frame_hdr: bool, + rdynamic: bool, + z_nodelete: bool, + z_defs: bool, bind_global_refs_locally: bool, is_native_os: bool, pic: bool, valgrind: bool, stack_check: bool, single_threaded: bool, - verbose_link: bool = false, + verbose_link: bool, dll_export_fns: bool, error_return_tracing: bool, is_compiler_rt_or_libc: bool, each_lib_rpath: bool, disable_lld_caching: bool, + is_test: bool, gc_sections: ?bool = null, - allow_shlib_undefined: ?bool = null, - subsystem: ?std.Target.SubSystem = null, - linker_script: ?[]const u8 = null, - version_script: ?[]const u8 = null, - override_soname: ?[]const u8 = null, - llvm_cpu_features: ?[*:0]const u8 = null, + allow_shlib_undefined: ?bool, + subsystem: ?std.Target.SubSystem, + linker_script: ?[]const u8, + version_script: ?[]const u8, + override_soname: ?[]const u8, + llvm_cpu_features: ?[*:0]const u8, /// Extra args passed directly to LLD. Ignored when not linking with LLD. - extra_lld_args: []const []const u8 = &[0][]const u8, - - objects: []const []const u8 = &[0][]const u8{}, - framework_dirs: []const []const u8 = &[0][]const u8{}, - frameworks: []const []const u8 = &[0][]const u8{}, - system_libs: []const []const u8 = &[0][]const u8{}, - lib_dirs: []const []const u8 = &[0][]const u8{}, - rpath_list: []const []const u8 = &[0][]const u8{}, + extra_lld_args: []const []const u8, + + objects: []const []const u8, + framework_dirs: []const []const u8, + frameworks: []const []const u8, + system_libs: std.StringArrayHashMapUnmanaged(void), + lib_dirs: []const []const u8, + rpath_list: []const []const u8, version: ?std.builtin.Version, libc_installation: ?*const LibCInstallation, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index c396732bc1..e1d7a07fbc 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -5,6 +5,8 @@ const log = std.log.scoped(.link); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const fs = std.fs; +const allocPrint = std.fmt.allocPrint; +const mem = std.mem; const trace = @import("../tracy.zig").trace; const Module = @import("../Module.zig"); @@ -12,6 +14,8 @@ const Compilation = @import("../Compilation.zig"); const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); const build_options = @import("build_options"); +const Cache = @import("../Cache.zig"); +const mingw = @import("../mingw.zig"); const allocation_padding = 4 / 3; const minimum_text_block_size = 64 * allocation_padding; @@ -21,7 +25,7 @@ const file_alignment = 512; const image_base = 0x400_000; const section_table_size = 2 * 40; comptime { - assert(std.mem.isAligned(image_base, section_alignment)); + assert(mem.isAligned(image_base, section_alignment)); } pub const base_tag: link.File.Tag = .coff; @@ -155,14 +159,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio if (machine == .Unknown) { return error.UnsupportedCOFFArchitecture; } - std.mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine)); + mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine)); index += 2; // Number of sections (we only use .got, .text) - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2); + mem.writeIntLittle(u16, hdr_data[index..][0..2], 2); index += 2; // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32) - std.mem.set(u8, hdr_data[index..][0..12], 0); + mem.set(u8, hdr_data[index..][0..12], 0); index += 12; const optional_header_size = switch (options.output_mode) { @@ -177,8 +181,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const default_offset_table_size = file_alignment; const default_size_of_code = 0; - self.section_data_offset = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); - const section_data_relative_virtual_address = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); + self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); + const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); self.offset_table_virtual_address = image_base + section_data_relative_virtual_address; self.offset_table_size = default_offset_table_size; self.section_table_offset = section_table_offset; @@ -186,9 +190,9 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio self.text_section_size = default_size_of_code; // Size of file when loaded in memory - const size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment); + const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment); - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); + mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); index += 2; // Characteristics @@ -200,7 +204,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE, .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE, } - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics); + mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics); index += 2; assert(index == 20); @@ -210,106 +214,106 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio self.optional_header_offset = coff_file_header_offset + 20; // Optional header index = 0; - std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) { + mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) { .p32 => @as(u16, 0x10b), .p64 => 0x20b, }); index += 2; // Linker version (u8 + u8) - std.mem.set(u8, hdr_data[index..][0..2], 0); + mem.set(u8, hdr_data[index..][0..2], 0); index += 2; // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32) - std.mem.set(u8, hdr_data[index..][0..20], 0); + mem.set(u8, hdr_data[index..][0..20], 0); index += 20; if (self.ptr_width == .p32) { // Base of data relative to the image base (UNUSED) - std.mem.set(u8, hdr_data[index..][0..4], 0); + mem.set(u8, hdr_data[index..][0..4], 0); index += 4; // Image base address - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base); + mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base); index += 4; } else { // Image base address - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base); + mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base); index += 8; } // Section alignment - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment); + mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment); index += 4; // File alignment - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment); + mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment); index += 4; // Required OS version, 6.0 is vista - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); + mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); index += 2; - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); index += 2; // Image version - std.mem.set(u8, hdr_data[index..][0..4], 0); + mem.set(u8, hdr_data[index..][0..4], 0); index += 4; // Required subsystem version, same as OS version - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); + mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); index += 2; - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); index += 2; // Reserved zeroes (u32) - std.mem.set(u8, hdr_data[index..][0..4], 0); + mem.set(u8, hdr_data[index..][0..4], 0); index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image); + mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image); index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); index += 4; // CheckSum (u32) - std.mem.set(u8, hdr_data[index..][0..4], 0); + mem.set(u8, hdr_data[index..][0..4], 0); index += 4; // Subsystem, TODO: Let users specify the subsystem, always CUI for now - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3); + mem.writeIntLittle(u16, hdr_data[index..][0..2], 3); index += 2; // DLL characteristics - std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0); + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0); index += 2; switch (self.ptr_width) { .p32 => { // Size of stack reserve + commit - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000); + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000); index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); index += 4; // Size of heap reserve + commit - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000); + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000); index += 4; - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); index += 4; }, .p64 => { // Size of stack reserve + commit - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000); + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000); index += 8; - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); index += 8; // Size of heap reserve + commit - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000); + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000); index += 8; - std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); index += 8; }, } // Reserved zeroes - std.mem.set(u8, hdr_data[index..][0..4], 0); + mem.set(u8, hdr_data[index..][0..4], 0); index += 4; // Number of data directories - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count); + mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count); index += 4; // Initialize data directories to zero - std.mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0); + mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0); index += data_directory_count * 8; assert(index == optional_header_size); @@ -321,52 +325,52 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio index += 8; if (options.output_mode == .Exe) { // Virtual size (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); index += 4; // Virtual address (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base); + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base); index += 4; } else { - std.mem.set(u8, hdr_data[index..][0..8], 0); + mem.set(u8, hdr_data[index..][0..8], 0); index += 8; } // Size of raw data (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); index += 4; // File pointer to the start of the section - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); index += 4; // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) - std.mem.set(u8, hdr_data[index..][0..12], 0); + mem.set(u8, hdr_data[index..][0..12], 0); index += 12; // Section flags - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ); + mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ); index += 4; // Then, the .text section hdr_data[index..][0..8].* = ".text\x00\x00\x00".*; index += 8; if (options.output_mode == .Exe) { // Virtual size (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); index += 4; // Virtual address (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base); + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base); index += 4; } else { - std.mem.set(u8, hdr_data[index..][0..8], 0); + mem.set(u8, hdr_data[index..][0..8], 0); index += 8; } // Size of raw data (u32) - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); index += 4; // File pointer to the start of the section - std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size); + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size); index += 4; // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) - std.mem.set(u8, hdr_data[index..][0..12], 0); + mem.set(u8, hdr_data[index..][0..12], 0); index += 12; // Section flags - std.mem.writeIntLittle( + mem.writeIntLittle( u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE, @@ -434,7 +438,7 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a const free_block = self.text_block_free_list.items[i]; const next_block_text_offset = free_block.text_offset + free_block.capacity(); - const new_block_text_offset = std.mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address; + const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address; if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) { block_placement = free_block; @@ -453,7 +457,7 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a continue; } } else if (self.last_text_block) |last| { - const new_block_vaddr = std.mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment); + const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment); block_placement = last; break :blk new_block_vaddr; } else { @@ -463,15 +467,15 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a const expand_text_section = block_placement == null or block_placement.?.next == null; if (expand_text_section) { - const needed_size = @intCast(u32, std.mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment)); + const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment)); if (needed_size > self.text_section_size) { - const current_text_section_virtual_size = std.mem.alignForwardGeneric(u32, self.text_section_size, section_alignment); - const new_text_section_virtual_size = std.mem.alignForwardGeneric(u32, needed_size, section_alignment); + const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment); + const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment); if (current_text_section_virtual_size != new_text_section_virtual_size) { self.size_of_image_dirty = true; // Write new virtual size var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, new_text_section_virtual_size); + mem.writeIntLittle(u32, &buf, new_text_section_virtual_size); try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8); } @@ -509,7 +513,7 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { const block_vaddr = text_block.getVAddr(self.*); - const align_ok = std.mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr; + const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr; const need_realloc = !align_ok or new_block_size > text_block.capacity(); if (!need_realloc) return @as(u64, block_vaddr); return self.allocateTextBlock(text_block, new_block_size, alignment); @@ -575,14 +579,14 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { // Write the new raw size in the .got header var buf: [8]u8 = undefined; - std.mem.writeIntLittle(u32, buf[0..4], new_raw_size); + mem.writeIntLittle(u32, buf[0..4], new_raw_size); try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16); // Write the new .text section file offset in the .text section header - std.mem.writeIntLittle(u32, buf[0..4], new_text_section_start); + mem.writeIntLittle(u32, buf[0..4], new_text_section_start); try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20); - const current_virtual_size = std.mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment); - const new_virtual_size = std.mem.alignForwardGeneric(u32, new_raw_size, section_alignment); + const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment); + const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment); // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section // and the virutal size of the `.got` section @@ -592,12 +596,12 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { const va_offset = new_virtual_size - current_virtual_size; // Write .got virtual size - std.mem.writeIntLittle(u32, buf[0..4], new_virtual_size); + mem.writeIntLittle(u32, buf[0..4], new_virtual_size); try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8); // Write .text new virtual address self.text_section_virtual_address = self.text_section_virtual_address + va_offset; - std.mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base); + mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base); try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12); // Fix the VAs in the offset table @@ -607,11 +611,11 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { switch (entry_size) { 4 => { - std.mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian); + mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian); try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size); }, 8 => { - std.mem.writeInt(u64, &buf, va.*, endian); + mem.writeInt(u64, &buf, va.*, endian); try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size); }, else => unreachable, @@ -626,12 +630,12 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { switch (entry_size) { 4 => { var buf: [4]u8 = undefined; - std.mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); + mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); }, 8 => { var buf: [8]u8 = undefined; - std.mem.writeInt(u64, &buf, self.offset_table.items[index], endian); + mem.writeInt(u64, &buf, self.offset_table.items[index], endian); try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); }, else => unreachable, @@ -664,7 +668,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { if (curr_size != 0) { const capacity = decl.link.coff.capacity(); const need_realloc = code.len > capacity or - !std.mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment); + !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment); if (need_realloc) { const curr_vaddr = self.getDeclVAddr(decl); const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); @@ -679,7 +683,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { } } else { const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ std.mem.spanZ(decl.name), vaddr, code.len }); + log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); errdefer self.freeTextBlock(&decl.link.coff); self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); @@ -702,7 +706,7 @@ pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void { for (exports) |exp| { if (exp.options.section) |section_name| { - if (!std.mem.eql(u8, section_name, ".text")) { + if (!mem.eql(u8, section_name, ".text")) { try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, @@ -711,7 +715,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, continue; } } - if (std.mem.eql(u8, exp.options.name, "_start")) { + if (mem.eql(u8, exp.options.name, "_start")) { self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base; } else { try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); @@ -726,8 +730,12 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, pub fn flush(self: *Coff, comp: *Compilation) !void { if (build_options.have_llvm and self.base.options.use_lld) { - return error.CoffLinkingWithLLDUnimplemented; + return self.linkWithLLD(comp); } else { + switch (self.base.options.effectiveOutputMode()) { + .Exe, .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } return self.flushModule(comp); } } @@ -739,16 +747,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { if (self.text_section_size_dirty) { // Write the new raw size in the .text header var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, self.text_section_size); + mem.writeIntLittle(u32, &buf, self.text_section_size); try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16); try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size); self.text_section_size_dirty = false; } if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) { - const new_size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment); + const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment); var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, new_size_of_image); + mem.writeIntLittle(u32, &buf, new_size_of_image); try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56); self.size_of_image_dirty = false; } @@ -763,12 +771,422 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { if (self.base.options.output_mode == .Exe) { // Write AddressOfEntryPoint var buf: [4]u8 = undefined; - std.mem.writeIntLittle(u32, &buf, self.entry_addr.?); + mem.writeIntLittle(u32, &buf, self.entry_addr.?); try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16); } } } +fn linkWithLLD(self: *Coff, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const is_lib = self.base.options.output_mode == .Lib; + const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; + const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe; + const target = self.base.options.target; + + // See link/Elf.zig for comments on how this mechanism works. + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + self.base.releaseLock(); + + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + man.hash.addOptional(self.base.options.stack_size_override); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.add(self.base.options.is_compiler_rt_or_libc); + if (self.base.options.link_libc) { + man.hash.add(self.base.options.libc_installation != null); + if (self.base.options.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + if (target.abi == .msvc) { + man.hash.addBytes(libc_installation.msvc_lib_dir.?); + man.hash.addBytes(libc_installation.kernel32_lib_dir.?); + } + } + } + man.hash.addStringSet(self.base.options.system_libs); + man.hash.addOptional(self.base.options.subsystem); + man.hash.add(self.base.options.is_test); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("COFF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("COFF LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("COFF LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + const is_obj = self.base.options.output_mode == .Obj; + + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + if (is_obj) { + try argv.append("-r"); + } + + try argv.append("-ERRORLIMIT:0"); + try argv.append("-NOLOGO"); + if (!self.base.options.strip) { + try argv.append("-DEBUG"); + } + if (self.base.options.output_mode == .Exe) { + const stack_size = self.base.options.stack_size_override orelse 16777216; + try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); + } + + if (target.cpu.arch == .i386) { + try argv.append("-MACHINE:X86"); + } else if (target.cpu.arch == .x86_64) { + try argv.append("-MACHINE:X64"); + } else if (target.cpu.arch.isARM()) { + if (target.cpu.arch.ptrBitWidth() == 32) { + try argv.append("-MACHINE:ARM"); + } else { + try argv.append("-MACHINE:ARM64"); + } + } + + if (is_dyn_lib) { + try argv.append("-DLL"); + } + + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); + + if (self.base.options.link_libc) { + if (self.base.options.libc_installation) |libc_installation| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); + + if (target.abi == .msvc) { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); + } + } + } + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); + } + + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + const resolved_subsystem: ?std.Target.SubSystem = blk: { + if (self.base.options.subsystem) |explicit| break :blk explicit; + switch (target.os.tag) { + .windows => { + if (self.base.options.module) |module| { + if (module.have_dllmain_crt_startup or is_dyn_lib) + break :blk null; + if (module.have_c_main or self.base.options.is_test or + module.have_winmain_crt_startup or module.have_wwinmain_crt_startup) + { + break :blk .Console; + } + if (module.have_winmain or module.have_wwinmain) + break :blk .Windows; + } + }, + .uefi => break :blk .EfiApplication, + else => {}, + } + break :blk null; + }; + const Mode = enum { uefi, win32 }; + const mode: Mode = mode: { + if (resolved_subsystem) |subsystem| switch (subsystem) { + .Console => { + try argv.append("-SUBSYSTEM:console"); + break :mode .win32; + }, + .EfiApplication => { + try argv.append("-SUBSYSTEM:efi_application"); + break :mode .uefi; + }, + .EfiBootServiceDriver => { + try argv.append("-SUBSYSTEM:efi_boot_service_driver"); + break :mode .uefi; + }, + .EfiRom => { + try argv.append("-SUBSYSTEM:efi_rom"); + break :mode .uefi; + }, + .EfiRuntimeDriver => { + try argv.append("-SUBSYSTEM:efi_runtime_driver"); + break :mode .uefi; + }, + .Native => { + try argv.append("-SUBSYSTEM:native"); + break :mode .win32; + }, + .Posix => { + try argv.append("-SUBSYSTEM:posix"); + break :mode .win32; + }, + .Windows => { + try argv.append("-SUBSYSTEM:windows"); + break :mode .win32; + }, + } else if (target.os.tag == .uefi) { + break :mode .uefi; + } else { + break :mode .win32; + } + }; + + switch (mode) { + .uefi => try argv.appendSlice(&[_][]const u8{ + "-BASE:0", + "-ENTRY:EfiMain", + "-OPT:REF", + "-SAFESEH:NO", + "-MERGE:.rdata=.data", + "-ALIGN:32", + "-NODEFAULTLIB", + "-SECTION:.xdata,D", + }), + .win32 => { + if (link_in_crt) { + if (target.abi.isGnu()) { + try argv.append("-lldmingw"); + + if (target.cpu.arch == .i386) { + try argv.append("-ALTERNATENAME:__image_base__=___ImageBase"); + } else { + try argv.append("-ALTERNATENAME:__image_base__=__ImageBase"); + } + + if (is_dyn_lib) { + try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.o")); + } else { + try argv.append(try comp.get_libc_crt_file(arena, "crt2.o")); + } + + try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib")); + try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib")); + try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib")); + + for (mingw.always_link_libs) |name| { + if (!self.base.options.system_libs.contains(name)) { + const lib_basename = try allocPrint(arena, "{s}.lib", .{name}); + try argv.append(try comp.get_libc_crt_file(arena, lib_basename)); + } + } + } else { + const lib_str = switch (self.base.options.link_mode) { + .Dynamic => "", + .Static => "lib", + }; + const d_str = switch (self.base.options.optimize_mode) { + .Debug => "d", + else => "", + }; + switch (self.base.options.link_mode) { + .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})), + .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})), + } + + try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str })); + try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str })); + + //Visual C++ 2015 Conformance Changes + //https://msdn.microsoft.com/en-us/library/bb531344.aspx + try argv.append("legacy_stdio_definitions.lib"); + + // msvcrt depends on kernel32 and ntdll + try argv.append("kernel32.lib"); + try argv.append("ntdll.lib"); + } + } else { + try argv.append("-NODEFAULTLIB"); + if (!is_lib) { + if (self.base.options.module) |module| { + if (module.have_winmain) { + try argv.append("-ENTRY:WinMain"); + } else if (module.have_wwinmain) { + try argv.append("-ENTRY:wWinMain"); + } else if (module.have_wwinmain_crt_startup) { + try argv.append("-ENTRY:wWinMainCRTStartup"); + } else { + try argv.append("-ENTRY:WinMainCRTStartup"); + } + } else { + try argv.append("-ENTRY:WinMainCRTStartup"); + } + } + } + }, + } + + if (!is_obj) { + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } + } + + // compiler-rt and libc + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + if (!self.base.options.link_libc) { + try argv.append(comp.libc_static_lib.?.full_object_path); + } + // MSVC compiler_rt is missing some stuff, so we build it unconditionally but + // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + for (self.base.options.system_libs.items()) |entry| { + const lib_basename = try allocPrint(arena, "{s}.lib", .{entry.key}); + if (comp.crt_files.get(lib_basename)) |crt_file| { + try argv.append(crt_file.full_object_path); + } else { + try argv.append(lib_basename); + } + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); + new_argv_with_sentinel[argv.items.len] = null; + const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .coff = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .coff = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .COFF, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + coff: *Coff, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { return self.text_section_virtual_address + decl.link.coff.text_offset; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c8067058d9..88f5040761 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1225,7 +1225,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; if (use_stage1) { - const obj_basename = try std.fmt.allocPrint(arena, "{}.o", .{self.base.options.root_name}); + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); const o_directory = self.base.options.module.?.zig_cache_artifact_directory; const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); break :blk full_obj_path; @@ -1242,6 +1246,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; const have_dynamic_linker = self.base.options.link_libc and self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib; + const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe; + const target = self.base.options.target; // Here we want to determine whether we can save time by not invoking LLD when the // output is unchanged. None of the linker options or the object files that are being @@ -1297,7 +1303,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { man.hash.addOptionalBytes(self.base.options.override_soname); man.hash.addOptional(self.base.options.version); } - man.hash.addListOfBytes(self.base.options.system_libs); + man.hash.addStringSet(self.base.options.system_libs); man.hash.addOptional(self.base.options.allow_shlib_undefined); man.hash.add(self.base.options.bind_global_refs_locally); @@ -1326,7 +1332,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { }; } - const target = self.base.options.target; const is_obj = self.base.options.output_mode == .Obj; // Create an LLD command line and invoke it. @@ -1337,7 +1342,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (is_obj) { try argv.append("-r"); } - const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe; try argv.append("-error-limit=0"); @@ -1440,7 +1444,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { var test_path = std.ArrayList(u8).init(self.base.allocator); defer test_path.deinit(); for (self.base.options.lib_dirs) |lib_dir_path| { - for (self.base.options.system_libs) |link_lib| { + for (self.base.options.system_libs.items()) |link_lib| { test_path.shrinkRetainingCapacity(0); const sep = fs.path.sep_str; try test_path.writer().print("{}" ++ sep ++ "lib{}.so", .{ lib_dir_path, link_lib }); @@ -1509,8 +1513,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } // Shared libraries. - try argv.ensureCapacity(argv.items.len + self.base.options.system_libs.len); - for (self.base.options.system_libs) |link_lib| { + const system_libs = self.base.options.system_libs.items(); + try argv.ensureCapacity(argv.items.len + system_libs.len); + for (system_libs) |entry| { + const link_lib = entry.key; // By this time, we depend on these libs being dynamically linked libraries and not static libraries // (the check for that needs to be earlier), but they could be full paths to .so files, in which // case we want to avoid prepending "-l". @@ -1581,10 +1587,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } if (self.base.options.verbose_link) { - for (argv.items[0 .. argv.items.len - 1]) |arg| { - std.debug.print("{} ", .{arg}); - } - std.debug.print("{}\n", .{argv.items[argv.items.len - 1]}); + Compilation.dump_argv(argv.items); } // Oh, snapplesauce! We need null terminated argv. diff --git a/src/mingw.zig b/src/mingw.zig new file mode 100644 index 0000000000..2a2879de05 --- /dev/null +++ b/src/mingw.zig @@ -0,0 +1,866 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); + +pub const CRTFile = enum { + crt2_o, + dllcrt2_o, + mingw32_lib, + msvcrt_os_lib, + mingwex_lib, + uuid_lib, +}; + +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + switch (crt_file) { + .crt2_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-U__CRTDLL__", + "-D__MSVCRT__", + // Uncomment these 3 things for crtu + //"-DUNICODE", + //"-D_UNICODE", + //"-DWPRFLAG=1", + }); + return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", "crtexe.c", + }), + .extra_flags = args.items, + }, + }); + }, + + .dllcrt2_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-U__CRTDLL__", + "-D__MSVCRT__", + }); + return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", "crtdll.c", + }), + .extra_flags = args.items, + }, + }); + }, + + .mingw32_lib => { + var c_source_files: [mingw32_lib_deps.len]Compilation.CSourceFile = undefined; + for (mingw32_lib_deps) |dep, i| { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-DHAVE_CONFIG_H", + "-D_SYSCRT=1", + "-DCRTDLL=1", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "include", "any-windows-any", + }), + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + }); + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", dep, + }), + .extra_flags = args.items, + }; + } + return comp.build_crt_file("mingw32", .Lib, &c_source_files); + }, + + .msvcrt_os_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + "-D__LIBMSVCRT__", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + + "-g", + "-O2", + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + + for (msvcrt_common_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", dep }), + .extra_flags = extra_flags, + }; + } + if (comp.getTarget().cpu.arch == .i386) { + for (msvcrt_i386_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else { + for (msvcrt_other_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } + return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items); + }, + + .mingwex_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + + for (mingwex_generic_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + const target = comp.getTarget(); + if (target.cpu.arch == .i386 or target.cpu.arch == .x86_64) { + for (mingwex_x86_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else if (target.cpu.arch.isARM()) { + if (target.cpu.arch.ptrBitWidth() == 32) { + for (mingwex_arm32_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else { + for (mingwex_arm64_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } + } else { + unreachable; + } + return comp.build_crt_file("mingwex", .Lib, c_source_files.items); + }, + + .uuid_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "include", "any-windows-any", + }), + }); + var c_source_files: [uuid_src.len]Compilation.CSourceFile = undefined; + for (uuid_src) |dep, i| { + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "libsrc", dep, + }), + .extra_flags = extra_flags, + }; + } + return comp.build_crt_file("uuid", .Lib, &c_source_files); + }, + } +} + +fn add_cc_args( + comp: *Compilation, + arena: *Allocator, + args: *std.ArrayList([]const u8), +) error{OutOfMemory}!void { + try args.appendSlice(&[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + }); + + const target = comp.getTarget(); + if (target.cpu.arch.isARM() and target.cpu.arch.ptrBitWidth() == 32) { + try args.append("-mfpu=vfp"); + } + + try args.appendSlice(&[_][]const u8{ + "-std=gnu11", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + }); +} + +const mingw32_lib_deps = [_][]const u8{ + "crt0_c.c", + "dll_argv.c", + "gccmain.c", + "natstart.c", + "pseudo-reloc-list.c", + "wildcard.c", + "charmax.c", + "crt0_w.c", + "dllargv.c", + "gs_support.c", + "_newmode.c", + "tlssup.c", + "xncommod.c", + "cinitexe.c", + "merr.c", + "usermatherr.c", + "pesect.c", + "udllargc.c", + "xthdloc.c", + "CRT_fp10.c", + "mingw_helpers.c", + "pseudo-reloc.c", + "udll_argv.c", + "xtxtmode.c", + "crt_handler.c", + "tlsthrd.c", + "tlsmthread.c", + "tlsmcrt.c", + "cxa_atexit.c", +}; +const msvcrt_common_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "_create_locale.c", + "misc" ++ path.sep_str ++ "_free_locale.c", + "misc" ++ path.sep_str ++ "onexit_table.c", + "misc" ++ path.sep_str ++ "register_tls_atexit.c", + "stdio" ++ path.sep_str ++ "acrt_iob_func.c", + "misc" ++ path.sep_str ++ "_configthreadlocale.c", + "misc" ++ path.sep_str ++ "_get_current_locale.c", + "misc" ++ path.sep_str ++ "invalid_parameter_handler.c", + "misc" ++ path.sep_str ++ "output_format.c", + "misc" ++ path.sep_str ++ "purecall.c", + "secapi" ++ path.sep_str ++ "_access_s.c", + "secapi" ++ path.sep_str ++ "_cgets_s.c", + "secapi" ++ path.sep_str ++ "_cgetws_s.c", + "secapi" ++ path.sep_str ++ "_chsize_s.c", + "secapi" ++ path.sep_str ++ "_controlfp_s.c", + "secapi" ++ path.sep_str ++ "_cprintf_s.c", + "secapi" ++ path.sep_str ++ "_cprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_ctime32_s.c", + "secapi" ++ path.sep_str ++ "_ctime64_s.c", + "secapi" ++ path.sep_str ++ "_cwprintf_s.c", + "secapi" ++ path.sep_str ++ "_cwprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_gmtime32_s.c", + "secapi" ++ path.sep_str ++ "_gmtime64_s.c", + "secapi" ++ path.sep_str ++ "_localtime32_s.c", + "secapi" ++ path.sep_str ++ "_localtime64_s.c", + "secapi" ++ path.sep_str ++ "_mktemp_s.c", + "secapi" ++ path.sep_str ++ "_sopen_s.c", + "secapi" ++ path.sep_str ++ "_strdate_s.c", + "secapi" ++ path.sep_str ++ "_strtime_s.c", + "secapi" ++ path.sep_str ++ "_umask_s.c", + "secapi" ++ path.sep_str ++ "_vcprintf_s.c", + "secapi" ++ path.sep_str ++ "_vcprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_vcwprintf_s.c", + "secapi" ++ path.sep_str ++ "_vcwprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_vscprintf_p.c", + "secapi" ++ path.sep_str ++ "_vscwprintf_p.c", + "secapi" ++ path.sep_str ++ "_vswprintf_p.c", + "secapi" ++ path.sep_str ++ "_waccess_s.c", + "secapi" ++ path.sep_str ++ "_wasctime_s.c", + "secapi" ++ path.sep_str ++ "_wctime32_s.c", + "secapi" ++ path.sep_str ++ "_wctime64_s.c", + "secapi" ++ path.sep_str ++ "_wstrtime_s.c", + "secapi" ++ path.sep_str ++ "_wmktemp_s.c", + "secapi" ++ path.sep_str ++ "_wstrdate_s.c", + "secapi" ++ path.sep_str ++ "asctime_s.c", + "secapi" ++ path.sep_str ++ "memcpy_s.c", + "secapi" ++ path.sep_str ++ "memmove_s.c", + "secapi" ++ path.sep_str ++ "rand_s.c", + "secapi" ++ path.sep_str ++ "sprintf_s.c", + "secapi" ++ path.sep_str ++ "strerror_s.c", + "secapi" ++ path.sep_str ++ "vsprintf_s.c", + "secapi" ++ path.sep_str ++ "wmemcpy_s.c", + "secapi" ++ path.sep_str ++ "wmemmove_s.c", + "stdio" ++ path.sep_str ++ "mingw_lock.c", +}; +const msvcrt_i386_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "lc_locale_func.c", + "misc" ++ path.sep_str ++ "___mb_cur_max_func.c", +}; + +const msvcrt_other_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "__p___argv.c", + "misc" ++ path.sep_str ++ "__p__acmdln.c", + "misc" ++ path.sep_str ++ "__p__fmode.c", + "misc" ++ path.sep_str ++ "__p__wcmdln.c", +}; +const mingwex_generic_src = [_][]const u8{ + "complex" ++ path.sep_str ++ "_cabs.c", + "complex" ++ path.sep_str ++ "cabs.c", + "complex" ++ path.sep_str ++ "cabsf.c", + "complex" ++ path.sep_str ++ "cabsl.c", + "complex" ++ path.sep_str ++ "cacos.c", + "complex" ++ path.sep_str ++ "cacosf.c", + "complex" ++ path.sep_str ++ "cacosl.c", + "complex" ++ path.sep_str ++ "carg.c", + "complex" ++ path.sep_str ++ "cargf.c", + "complex" ++ path.sep_str ++ "cargl.c", + "complex" ++ path.sep_str ++ "casin.c", + "complex" ++ path.sep_str ++ "casinf.c", + "complex" ++ path.sep_str ++ "casinl.c", + "complex" ++ path.sep_str ++ "catan.c", + "complex" ++ path.sep_str ++ "catanf.c", + "complex" ++ path.sep_str ++ "catanl.c", + "complex" ++ path.sep_str ++ "ccos.c", + "complex" ++ path.sep_str ++ "ccosf.c", + "complex" ++ path.sep_str ++ "ccosl.c", + "complex" ++ path.sep_str ++ "cexp.c", + "complex" ++ path.sep_str ++ "cexpf.c", + "complex" ++ path.sep_str ++ "cexpl.c", + "complex" ++ path.sep_str ++ "cimag.c", + "complex" ++ path.sep_str ++ "cimagf.c", + "complex" ++ path.sep_str ++ "cimagl.c", + "complex" ++ path.sep_str ++ "clog.c", + "complex" ++ path.sep_str ++ "clog10.c", + "complex" ++ path.sep_str ++ "clog10f.c", + "complex" ++ path.sep_str ++ "clog10l.c", + "complex" ++ path.sep_str ++ "clogf.c", + "complex" ++ path.sep_str ++ "clogl.c", + "complex" ++ path.sep_str ++ "conj.c", + "complex" ++ path.sep_str ++ "conjf.c", + "complex" ++ path.sep_str ++ "conjl.c", + "complex" ++ path.sep_str ++ "cpow.c", + "complex" ++ path.sep_str ++ "cpowf.c", + "complex" ++ path.sep_str ++ "cpowl.c", + "complex" ++ path.sep_str ++ "cproj.c", + "complex" ++ path.sep_str ++ "cprojf.c", + "complex" ++ path.sep_str ++ "cprojl.c", + "complex" ++ path.sep_str ++ "creal.c", + "complex" ++ path.sep_str ++ "crealf.c", + "complex" ++ path.sep_str ++ "creall.c", + "complex" ++ path.sep_str ++ "csin.c", + "complex" ++ path.sep_str ++ "csinf.c", + "complex" ++ path.sep_str ++ "csinl.c", + "complex" ++ path.sep_str ++ "csqrt.c", + "complex" ++ path.sep_str ++ "csqrtf.c", + "complex" ++ path.sep_str ++ "csqrtl.c", + "complex" ++ path.sep_str ++ "ctan.c", + "complex" ++ path.sep_str ++ "ctanf.c", + "complex" ++ path.sep_str ++ "ctanl.c", + "crt" ++ path.sep_str ++ "dllentry.c", + "crt" ++ path.sep_str ++ "dllmain.c", + "gdtoa" ++ path.sep_str ++ "arithchk.c", + "gdtoa" ++ path.sep_str ++ "dmisc.c", + "gdtoa" ++ path.sep_str ++ "dtoa.c", + "gdtoa" ++ path.sep_str ++ "g__fmt.c", + "gdtoa" ++ path.sep_str ++ "g_dfmt.c", + "gdtoa" ++ path.sep_str ++ "g_ffmt.c", + "gdtoa" ++ path.sep_str ++ "g_xfmt.c", + "gdtoa" ++ path.sep_str ++ "gdtoa.c", + "gdtoa" ++ path.sep_str ++ "gethex.c", + "gdtoa" ++ path.sep_str ++ "gmisc.c", + "gdtoa" ++ path.sep_str ++ "hd_init.c", + "gdtoa" ++ path.sep_str ++ "hexnan.c", + "gdtoa" ++ path.sep_str ++ "misc.c", + "gdtoa" ++ path.sep_str ++ "qnan.c", + "gdtoa" ++ path.sep_str ++ "smisc.c", + "gdtoa" ++ path.sep_str ++ "strtodg.c", + "gdtoa" ++ path.sep_str ++ "strtodnrp.c", + "gdtoa" ++ path.sep_str ++ "strtof.c", + "gdtoa" ++ path.sep_str ++ "strtopx.c", + "gdtoa" ++ path.sep_str ++ "sum.c", + "gdtoa" ++ path.sep_str ++ "ulp.c", + "math" ++ path.sep_str ++ "abs64.c", + "math" ++ path.sep_str ++ "cbrt.c", + "math" ++ path.sep_str ++ "cbrtf.c", + "math" ++ path.sep_str ++ "cbrtl.c", + "math" ++ path.sep_str ++ "cephes_emath.c", + "math" ++ path.sep_str ++ "copysign.c", + "math" ++ path.sep_str ++ "copysignf.c", + "math" ++ path.sep_str ++ "coshf.c", + "math" ++ path.sep_str ++ "coshl.c", + "math" ++ path.sep_str ++ "erfl.c", + "math" ++ path.sep_str ++ "expf.c", + "math" ++ path.sep_str ++ "fabs.c", + "math" ++ path.sep_str ++ "fabsf.c", + "math" ++ path.sep_str ++ "fabsl.c", + "math" ++ path.sep_str ++ "fdim.c", + "math" ++ path.sep_str ++ "fdimf.c", + "math" ++ path.sep_str ++ "fdiml.c", + "math" ++ path.sep_str ++ "fma.c", + "math" ++ path.sep_str ++ "fmaf.c", + "math" ++ path.sep_str ++ "fmal.c", + "math" ++ path.sep_str ++ "fmax.c", + "math" ++ path.sep_str ++ "fmaxf.c", + "math" ++ path.sep_str ++ "fmaxl.c", + "math" ++ path.sep_str ++ "fmin.c", + "math" ++ path.sep_str ++ "fminf.c", + "math" ++ path.sep_str ++ "fminl.c", + "math" ++ path.sep_str ++ "fp_consts.c", + "math" ++ path.sep_str ++ "fp_constsf.c", + "math" ++ path.sep_str ++ "fp_constsl.c", + "math" ++ path.sep_str ++ "fpclassify.c", + "math" ++ path.sep_str ++ "fpclassifyf.c", + "math" ++ path.sep_str ++ "fpclassifyl.c", + "math" ++ path.sep_str ++ "frexpf.c", + "math" ++ path.sep_str ++ "hypot.c", + "math" ++ path.sep_str ++ "hypotf.c", + "math" ++ path.sep_str ++ "hypotl.c", + "math" ++ path.sep_str ++ "isnan.c", + "math" ++ path.sep_str ++ "isnanf.c", + "math" ++ path.sep_str ++ "isnanl.c", + "math" ++ path.sep_str ++ "ldexpf.c", + "math" ++ path.sep_str ++ "lgamma.c", + "math" ++ path.sep_str ++ "lgammaf.c", + "math" ++ path.sep_str ++ "lgammal.c", + "math" ++ path.sep_str ++ "llrint.c", + "math" ++ path.sep_str ++ "llrintf.c", + "math" ++ path.sep_str ++ "llrintl.c", + "math" ++ path.sep_str ++ "llround.c", + "math" ++ path.sep_str ++ "llroundf.c", + "math" ++ path.sep_str ++ "llroundl.c", + "math" ++ path.sep_str ++ "log10f.c", + "math" ++ path.sep_str ++ "logf.c", + "math" ++ path.sep_str ++ "lrint.c", + "math" ++ path.sep_str ++ "lrintf.c", + "math" ++ path.sep_str ++ "lrintl.c", + "math" ++ path.sep_str ++ "lround.c", + "math" ++ path.sep_str ++ "lroundf.c", + "math" ++ path.sep_str ++ "lroundl.c", + "math" ++ path.sep_str ++ "modf.c", + "math" ++ path.sep_str ++ "modff.c", + "math" ++ path.sep_str ++ "modfl.c", + "math" ++ path.sep_str ++ "nextafterf.c", + "math" ++ path.sep_str ++ "nextafterl.c", + "math" ++ path.sep_str ++ "nexttoward.c", + "math" ++ path.sep_str ++ "nexttowardf.c", + "math" ++ path.sep_str ++ "powf.c", + "math" ++ path.sep_str ++ "powi.c", + "math" ++ path.sep_str ++ "powif.c", + "math" ++ path.sep_str ++ "powil.c", + "math" ++ path.sep_str ++ "rint.c", + "math" ++ path.sep_str ++ "rintf.c", + "math" ++ path.sep_str ++ "rintl.c", + "math" ++ path.sep_str ++ "round.c", + "math" ++ path.sep_str ++ "roundf.c", + "math" ++ path.sep_str ++ "roundl.c", + "math" ++ path.sep_str ++ "s_erf.c", + "math" ++ path.sep_str ++ "sf_erf.c", + "math" ++ path.sep_str ++ "signbit.c", + "math" ++ path.sep_str ++ "signbitf.c", + "math" ++ path.sep_str ++ "signbitl.c", + "math" ++ path.sep_str ++ "signgam.c", + "math" ++ path.sep_str ++ "sinhf.c", + "math" ++ path.sep_str ++ "sinhl.c", + "math" ++ path.sep_str ++ "sqrt.c", + "math" ++ path.sep_str ++ "sqrtf.c", + "math" ++ path.sep_str ++ "sqrtl.c", + "math" ++ path.sep_str ++ "tanhf.c", + "math" ++ path.sep_str ++ "tanhl.c", + "math" ++ path.sep_str ++ "tgamma.c", + "math" ++ path.sep_str ++ "tgammaf.c", + "math" ++ path.sep_str ++ "tgammal.c", + "math" ++ path.sep_str ++ "truncl.c", + "misc" ++ path.sep_str ++ "alarm.c", + "misc" ++ path.sep_str ++ "basename.c", + "misc" ++ path.sep_str ++ "btowc.c", + "misc" ++ path.sep_str ++ "delay-f.c", + "misc" ++ path.sep_str ++ "delay-n.c", + "misc" ++ path.sep_str ++ "delayimp.c", + "misc" ++ path.sep_str ++ "dirent.c", + "misc" ++ path.sep_str ++ "dirname.c", + "misc" ++ path.sep_str ++ "feclearexcept.c", + "misc" ++ path.sep_str ++ "fegetenv.c", + "misc" ++ path.sep_str ++ "fegetexceptflag.c", + "misc" ++ path.sep_str ++ "fegetround.c", + "misc" ++ path.sep_str ++ "feholdexcept.c", + "misc" ++ path.sep_str ++ "feraiseexcept.c", + "misc" ++ path.sep_str ++ "fesetenv.c", + "misc" ++ path.sep_str ++ "fesetexceptflag.c", + "misc" ++ path.sep_str ++ "fesetround.c", + "misc" ++ path.sep_str ++ "fetestexcept.c", + "misc" ++ path.sep_str ++ "feupdateenv.c", + "misc" ++ path.sep_str ++ "ftruncate.c", + "misc" ++ path.sep_str ++ "ftw.c", + "misc" ++ path.sep_str ++ "ftw64.c", + "misc" ++ path.sep_str ++ "fwide.c", + "misc" ++ path.sep_str ++ "getlogin.c", + "misc" ++ path.sep_str ++ "getopt.c", + "misc" ++ path.sep_str ++ "gettimeofday.c", + "misc" ++ path.sep_str ++ "imaxabs.c", + "misc" ++ path.sep_str ++ "imaxdiv.c", + "misc" ++ path.sep_str ++ "isblank.c", + "misc" ++ path.sep_str ++ "iswblank.c", + "misc" ++ path.sep_str ++ "mbrtowc.c", + "misc" ++ path.sep_str ++ "mbsinit.c", + "misc" ++ path.sep_str ++ "mempcpy.c", + "misc" ++ path.sep_str ++ "mingw-aligned-malloc.c", + "misc" ++ path.sep_str ++ "mingw-fseek.c", + "misc" ++ path.sep_str ++ "mingw_getsp.S", + "misc" ++ path.sep_str ++ "mingw_matherr.c", + "misc" ++ path.sep_str ++ "mingw_mbwc_convert.c", + "misc" ++ path.sep_str ++ "mingw_usleep.c", + "misc" ++ path.sep_str ++ "mingw_wcstod.c", + "misc" ++ path.sep_str ++ "mingw_wcstof.c", + "misc" ++ path.sep_str ++ "mingw_wcstold.c", + "misc" ++ path.sep_str ++ "mkstemp.c", + "misc" ++ path.sep_str ++ "seterrno.c", + "misc" ++ path.sep_str ++ "sleep.c", + "misc" ++ path.sep_str ++ "strnlen.c", + "misc" ++ path.sep_str ++ "strsafe.c", + "misc" ++ path.sep_str ++ "strtoimax.c", + "misc" ++ path.sep_str ++ "strtold.c", + "misc" ++ path.sep_str ++ "strtoumax.c", + "misc" ++ path.sep_str ++ "tdelete.c", + "misc" ++ path.sep_str ++ "tfind.c", + "misc" ++ path.sep_str ++ "tsearch.c", + "misc" ++ path.sep_str ++ "twalk.c", + "misc" ++ path.sep_str ++ "uchar_c16rtomb.c", + "misc" ++ path.sep_str ++ "uchar_c32rtomb.c", + "misc" ++ path.sep_str ++ "uchar_mbrtoc16.c", + "misc" ++ path.sep_str ++ "uchar_mbrtoc32.c", + "misc" ++ path.sep_str ++ "wassert.c", + "misc" ++ path.sep_str ++ "wcrtomb.c", + "misc" ++ path.sep_str ++ "wcsnlen.c", + "misc" ++ path.sep_str ++ "wcstof.c", + "misc" ++ path.sep_str ++ "wcstoimax.c", + "misc" ++ path.sep_str ++ "wcstold.c", + "misc" ++ path.sep_str ++ "wcstoumax.c", + "misc" ++ path.sep_str ++ "wctob.c", + "misc" ++ path.sep_str ++ "wctrans.c", + "misc" ++ path.sep_str ++ "wctype.c", + "misc" ++ path.sep_str ++ "wdirent.c", + "misc" ++ path.sep_str ++ "winbs_uint64.c", + "misc" ++ path.sep_str ++ "winbs_ulong.c", + "misc" ++ path.sep_str ++ "winbs_ushort.c", + "misc" ++ path.sep_str ++ "wmemchr.c", + "misc" ++ path.sep_str ++ "wmemcmp.c", + "misc" ++ path.sep_str ++ "wmemcpy.c", + "misc" ++ path.sep_str ++ "wmemmove.c", + "misc" ++ path.sep_str ++ "wmempcpy.c", + "misc" ++ path.sep_str ++ "wmemset.c", + "stdio" ++ path.sep_str ++ "_Exit.c", + "stdio" ++ path.sep_str ++ "_findfirst64i32.c", + "stdio" ++ path.sep_str ++ "_findnext64i32.c", + "stdio" ++ path.sep_str ++ "_fstat.c", + "stdio" ++ path.sep_str ++ "_fstat64i32.c", + "stdio" ++ path.sep_str ++ "_ftime.c", + "stdio" ++ path.sep_str ++ "_getc_nolock.c", + "stdio" ++ path.sep_str ++ "_getwc_nolock.c", + "stdio" ++ path.sep_str ++ "_putc_nolock.c", + "stdio" ++ path.sep_str ++ "_putwc_nolock.c", + "stdio" ++ path.sep_str ++ "_stat.c", + "stdio" ++ path.sep_str ++ "_stat64i32.c", + "stdio" ++ path.sep_str ++ "_wfindfirst64i32.c", + "stdio" ++ path.sep_str ++ "_wfindnext64i32.c", + "stdio" ++ path.sep_str ++ "_wstat.c", + "stdio" ++ path.sep_str ++ "_wstat64i32.c", + "stdio" ++ path.sep_str ++ "asprintf.c", + "stdio" ++ path.sep_str ++ "atoll.c", + "stdio" ++ path.sep_str ++ "fgetpos64.c", + "stdio" ++ path.sep_str ++ "fopen64.c", + "stdio" ++ path.sep_str ++ "fseeko32.c", + "stdio" ++ path.sep_str ++ "fseeko64.c", + "stdio" ++ path.sep_str ++ "fsetpos64.c", + "stdio" ++ path.sep_str ++ "ftello.c", + "stdio" ++ path.sep_str ++ "ftello64.c", + "stdio" ++ path.sep_str ++ "ftruncate64.c", + "stdio" ++ path.sep_str ++ "lltoa.c", + "stdio" ++ path.sep_str ++ "lltow.c", + "stdio" ++ path.sep_str ++ "lseek64.c", + "stdio" ++ path.sep_str ++ "mingw_asprintf.c", + "stdio" ++ path.sep_str ++ "mingw_fprintf.c", + "stdio" ++ path.sep_str ++ "mingw_fprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_fscanf.c", + "stdio" ++ path.sep_str ++ "mingw_fwscanf.c", + "stdio" ++ path.sep_str ++ "mingw_pformat.c", + "stdio" ++ path.sep_str ++ "mingw_pformatw.c", + "stdio" ++ path.sep_str ++ "mingw_printf.c", + "stdio" ++ path.sep_str ++ "mingw_printfw.c", + "stdio" ++ path.sep_str ++ "mingw_scanf.c", + "stdio" ++ path.sep_str ++ "mingw_snprintf.c", + "stdio" ++ path.sep_str ++ "mingw_snprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_sprintf.c", + "stdio" ++ path.sep_str ++ "mingw_sprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_sscanf.c", + "stdio" ++ path.sep_str ++ "mingw_swscanf.c", + "stdio" ++ path.sep_str ++ "mingw_vasprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vfprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vfprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vfscanf.c", + "stdio" ++ path.sep_str ++ "mingw_vprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vsnprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vsnprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vsprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vsprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_wscanf.c", + "stdio" ++ path.sep_str ++ "mingw_wvfscanf.c", + "stdio" ++ path.sep_str ++ "scanf.S", + "stdio" ++ path.sep_str ++ "snprintf.c", + "stdio" ++ path.sep_str ++ "snwprintf.c", + "stdio" ++ path.sep_str ++ "strtof.c", + "stdio" ++ path.sep_str ++ "strtok_r.c", + "stdio" ++ path.sep_str ++ "truncate.c", + "stdio" ++ path.sep_str ++ "ulltoa.c", + "stdio" ++ path.sep_str ++ "ulltow.c", + "stdio" ++ path.sep_str ++ "vasprintf.c", + "stdio" ++ path.sep_str ++ "vfscanf.c", + "stdio" ++ path.sep_str ++ "vfscanf2.S", + "stdio" ++ path.sep_str ++ "vfwscanf.c", + "stdio" ++ path.sep_str ++ "vfwscanf2.S", + "stdio" ++ path.sep_str ++ "vscanf.c", + "stdio" ++ path.sep_str ++ "vscanf2.S", + "stdio" ++ path.sep_str ++ "vsnprintf.c", + "stdio" ++ path.sep_str ++ "vsnwprintf.c", + "stdio" ++ path.sep_str ++ "vsscanf.c", + "stdio" ++ path.sep_str ++ "vsscanf2.S", + "stdio" ++ path.sep_str ++ "vswscanf.c", + "stdio" ++ path.sep_str ++ "vswscanf2.S", + "stdio" ++ path.sep_str ++ "vwscanf.c", + "stdio" ++ path.sep_str ++ "vwscanf2.S", + "stdio" ++ path.sep_str ++ "wtoll.c", +}; + +const mingwex_x86_src = [_][]const u8{ + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2l.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceill.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceil.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "copysignl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cos.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl_internal.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cossin.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2f.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1f.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floor.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmod.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogb.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "internal_logl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexp.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexpl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log10l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1p.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2f.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logb.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "pow.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "powl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainder.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquof.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquol.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquo.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbn.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sin.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl_internal.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "truncf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "trunc.S", +}; + +const mingwex_arm32_src = [_][]const u8{ + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "exp2.c", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "trunc.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "truncf.S", +}; + +const mingwex_arm64_src = [_][]const u8{ + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2f.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "truncf.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "trunc.S", +}; + +const uuid_src = [_][]const u8{ + "ativscp-uuid.c", + "atsmedia-uuid.c", + "bth-uuid.c", + "cguid-uuid.c", + "comcat-uuid.c", + "devguid.c", + "docobj-uuid.c", + "dxva-uuid.c", + "exdisp-uuid.c", + "extras-uuid.c", + "fwp-uuid.c", + "guid_nul.c", + "hlguids-uuid.c", + "hlink-uuid.c", + "mlang-uuid.c", + "msctf-uuid.c", + "mshtmhst-uuid.c", + "mshtml-uuid.c", + "msxml-uuid.c", + "netcon-uuid.c", + "ntddkbd-uuid.c", + "ntddmou-uuid.c", + "ntddpar-uuid.c", + "ntddscsi-uuid.c", + "ntddser-uuid.c", + "ntddstor-uuid.c", + "ntddvdeo-uuid.c", + "oaidl-uuid.c", + "objidl-uuid.c", + "objsafe-uuid.c", + "ocidl-uuid.c", + "oleacc-uuid.c", + "olectlid-uuid.c", + "oleidl-uuid.c", + "power-uuid.c", + "powrprof-uuid.c", + "uianimation-uuid.c", + "usbcamdi-uuid.c", + "usbiodef-uuid.c", + "uuid.c", + "vds-uuid.c", + "virtdisk-uuid.c", + "wia-uuid.c", +}; + +pub const always_link_libs = [_][]const u8{ + "advapi32", + "kernel32", + "msvcrt", + "ntdll", + "shell32", + "user32", +}; diff --git a/src/musl.zig b/src/musl.zig index d64d643227..ef4ea7236b 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -7,9 +7,6 @@ const assert = std.debug.assert; const target_util = @import("target.zig"); const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); -const trace = @import("tracy.zig").trace; -const Cache = @import("Cache.zig"); -const Package = @import("Package.zig"); pub const CRTFile = enum { crti_o, diff --git a/src/stage1.zig b/src/stage1.zig index 2124c23f14..8142ce901c 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -121,6 +121,14 @@ pub const Module = extern struct { verbose_cimport: bool, verbose_llvm_cpu_features: bool, + // Set by stage1 + have_c_main: bool, + have_winmain: bool, + have_wwinmain: bool, + have_winmain_crt_startup: bool, + have_wwinmain_crt_startup: bool, + have_dllmain_crt_startup: bool, + pub fn build_object(mod: *Module) void { zig_stage1_build_object(mod); } diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index a1d0cd0aa1..7a5016d004 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -2152,12 +2152,6 @@ struct CodeGen { uint32_t next_unresolved_index; unsigned pointer_size_bytes; bool is_big_endian; - bool have_c_main; - bool have_winmain; - bool have_wwinmain; - bool have_winmain_crt_startup; - bool have_wwinmain_crt_startup; - bool have_dllmain_crt_startup; bool have_err_ret_tracing; bool verbose_tokenize; bool verbose_ast; diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 15c30350d7..369c284684 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -3496,18 +3496,18 @@ void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLink void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) { if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->link_libc) { - g->have_c_main = true; + g->stage1.have_c_main = true; } else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) { if (strcmp(symbol_name, "WinMain") == 0) { - g->have_winmain = true; + g->stage1.have_winmain = true; } else if (strcmp(symbol_name, "wWinMain") == 0) { - g->have_wwinmain = true; + g->stage1.have_wwinmain = true; } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) { - g->have_winmain_crt_startup = true; + g->stage1.have_winmain_crt_startup = true; } else if (strcmp(symbol_name, "wWinMainCRTStartup") == 0) { - g->have_wwinmain_crt_startup = true; + g->stage1.have_wwinmain_crt_startup = true; } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) { - g->have_dllmain_crt_startup = true; + g->stage1.have_dllmain_crt_startup = true; } } diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index fff1c325fc..2fc85b42fe 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8666,11 +8666,11 @@ TargetSubsystem detect_subsystem(CodeGen *g) { if (g->subsystem != TargetSubsystemAuto) return g->subsystem; if (g->zig_target->os == OsWindows) { - if (g->have_dllmain_crt_startup) + if (g->stage1.have_dllmain_crt_startup) return TargetSubsystemAuto; - if (g->have_c_main || g->is_test_build || g->have_winmain_crt_startup || g->have_wwinmain_crt_startup) + if (g->stage1.have_c_main || g->is_test_build || g->stage1.have_winmain_crt_startup || g->stage1.have_wwinmain_crt_startup) return TargetSubsystemConsole; - if (g->have_winmain || g->have_wwinmain) + if (g->stage1.have_winmain || g->stage1.have_wwinmain) return TargetSubsystemWindows; } else if (g->zig_target->os == OsUefi) { return TargetSubsystemEfiApplication; diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h index efbd02e393..412118a6fa 100644 --- a/src/stage1/stage1.h +++ b/src/stage1/stage1.h @@ -195,6 +195,14 @@ struct ZigStage1 { bool verbose_llvm_ir; bool verbose_cimport; bool verbose_llvm_cpu_features; + + // Set by stage1 + bool have_c_main; + bool have_winmain; + bool have_wwinmain; + bool have_winmain_crt_startup; + bool have_wwinmain_crt_startup; + bool have_dllmain_crt_startup; }; ZIG_EXTERN_C void zig_stage1_os_init(void); -- cgit v1.2.3