9146 lines
373 KiB
C++
9146 lines
373 KiB
C++
/*
|
||
* 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<AstNode *>(0x1);
|
||
// this one means analyzed and it's not async
|
||
static const AstNode *inferred_async_none = reinterpret_cast<AstNode *>(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 = allocate<ZigType>(1);
|
||
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<ScopeExpr *>(scope);
|
||
case ScopeIdDefer:
|
||
case ScopeIdDeferExpr:
|
||
case ScopeIdDecls:
|
||
case ScopeIdFnDef:
|
||
case ScopeIdCompTime:
|
||
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;
|
||
}
|
||
|
||
static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
|
||
ZigType *import, Buf *bare_name)
|
||
{
|
||
ScopeDecls *scope = allocate<ScopeDecls>(1);
|
||
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 = allocate<ScopeBlock>(1);
|
||
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 = allocate<ScopeDefer>(1);
|
||
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 = allocate<ScopeDeferExpr>(1);
|
||
init_scope(g, &scope->base, ScopeIdDeferExpr, node, parent);
|
||
return scope;
|
||
}
|
||
|
||
Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) {
|
||
ScopeVarDecl *scope = allocate<ScopeVarDecl>(1);
|
||
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 = allocate<ScopeCImport>(1);
|
||
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 = allocate<ScopeLoop>(1);
|
||
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, IrInstruction *is_comptime) {
|
||
ScopeRuntime *scope = allocate<ScopeRuntime>(1);
|
||
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 = allocate<ScopeSuspend>(1);
|
||
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 = allocate<ScopeFnDef>(1);
|
||
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 = allocate<ScopeCompTime>(1);
|
||
init_scope(g, &scope->base, ScopeIdCompTime, node, parent);
|
||
return &scope->base;
|
||
}
|
||
|
||
Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) {
|
||
ScopeTypeOf *scope = allocate<ScopeTypeOf>(1);
|
||
init_scope(g, &scope->base, ScopeIdTypeOf, node, parent);
|
||
return &scope->base;
|
||
}
|
||
|
||
ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
|
||
ScopeExpr *scope = allocate<ScopeExpr>(1);
|
||
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 = reallocate_nonzero<ScopeExpr *>(
|
||
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<ScopeTypeOf *>(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 ZigTypeIdArgTuple:
|
||
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;
|
||
}
|
||
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;
|
||
}
|
||
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 ZigTypeIdArgTuple:
|
||
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.is_slice;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
static const char *ptr_len_to_star_str(PtrLen ptr_len) {
|
||
switch (ptr_len) {
|
||
case PtrLenSingle:
|
||
return "*";
|
||
case PtrLenUnknown:
|
||
return "[*]";
|
||
case PtrLenC:
|
||
return "[*c]";
|
||
}
|
||
zig_unreachable();
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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)
|
||
{
|
||
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)
|
||
{
|
||
type_id.id = ZigTypeIdPointer;
|
||
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;
|
||
|
||
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);
|
||
|
||
const char *star_str = ptr_len_to_star_str(ptr_len);
|
||
const char *const_str = is_const ? "const " : "";
|
||
const char *volatile_str = is_volatile ? "volatile " : "";
|
||
const char *allow_zero_str;
|
||
if (ptr_len == PtrLenC) {
|
||
assert(allow_zero);
|
||
allow_zero_str = "";
|
||
} else {
|
||
allow_zero_str = allow_zero ? "allowzero " : "";
|
||
}
|
||
buf_resize(&entry->name, 0);
|
||
if (host_int_bytes == 0 && byte_alignment == 0 && vector_index == VECTOR_INDEX_NONE) {
|
||
if (inferred_struct_field == nullptr) {
|
||
buf_appendf(&entry->name, "%s%s%s%s%s",
|
||
star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
|
||
} else {
|
||
buf_appendf(&entry->name, "(%s%s%s%s field '%s' of %s)",
|
||
star_str, const_str, volatile_str, allow_zero_str,
|
||
buf_ptr(inferred_struct_field->field_name),
|
||
buf_ptr(&inferred_struct_field->inferred_struct_type->name));
|
||
}
|
||
} else if (host_int_bytes == 0 && vector_index == VECTOR_INDEX_NONE) {
|
||
buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
|
||
const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
|
||
} else if (byte_alignment == 0) {
|
||
assert(vector_index == VECTOR_INDEX_NONE);
|
||
buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s",
|
||
star_str,
|
||
bit_offset_in_host, host_int_bytes,
|
||
const_str, volatile_str, allow_zero_str,
|
||
buf_ptr(&child_type->name));
|
||
} else if (vector_index == VECTOR_INDEX_NONE) {
|
||
buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s",
|
||
star_str, byte_alignment,
|
||
bit_offset_in_host, host_int_bytes,
|
||
const_str, volatile_str, allow_zero_str,
|
||
buf_ptr(&child_type->name));
|
||
} else if (vector_index == VECTOR_INDEX_RUNTIME) {
|
||
buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ":?) %s%s%s%s",
|
||
star_str, byte_alignment,
|
||
bit_offset_in_host, host_int_bytes,
|
||
const_str, volatile_str, allow_zero_str,
|
||
buf_ptr(&child_type->name));
|
||
} else {
|
||
buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s",
|
||
star_str, byte_alignment,
|
||
bit_offset_in_host, host_int_bytes, vector_index,
|
||
const_str, volatile_str, allow_zero_str,
|
||
buf_ptr(&child_type->name));
|
||
}
|
||
|
||
if (type_is_resolved(child_type, ResolveStatusZeroBitsKnown)) {
|
||
if (type_has_bits(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;
|
||
|
||
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);
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
|
||
if (child_type->optional_parent != nullptr) {
|
||
return child_type->optional_parent;
|
||
}
|
||
|
||
assert(type_is_resolved(child_type, ResolveStatusSizeKnown));
|
||
|
||
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(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(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(payload_type)) {
|
||
if (type_has_bits(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(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) {
|
||
TypeId type_id = {};
|
||
type_id.id = ZigTypeIdArray;
|
||
type_id.data.array.child_type = child_type;
|
||
type_id.data.array.size = array_size;
|
||
auto existing_entry = g->type_table.maybe_get(type_id);
|
||
if (existing_entry) {
|
||
return existing_entry->value;
|
||
}
|
||
|
||
assert(type_is_resolved(child_type, ResolveStatusSizeKnown));
|
||
|
||
ZigType *entry = new_type_table_entry(ZigTypeIdArray);
|
||
|
||
buf_resize(&entry->name, 0);
|
||
buf_appendf(&entry->name, "[%" ZIG_PRI_u64 "]%s", array_size, buf_ptr(&child_type->name));
|
||
|
||
entry->size_in_bits = child_type->size_in_bits * array_size;
|
||
entry->abi_align = child_type->abi_align;
|
||
entry->abi_size = child_type->abi_size * array_size;
|
||
|
||
entry->data.array.child_type = child_type;
|
||
entry->data.array.len = array_size;
|
||
|
||
g->type_table.put(type_id, entry);
|
||
return entry;
|
||
}
|
||
|
||
ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
|
||
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;
|
||
}
|
||
|
||
ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
|
||
|
||
// replace the & with [] to go from a ptr type name to a slice type name
|
||
buf_resize(&entry->name, 0);
|
||
size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3;
|
||
buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset);
|
||
|
||
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.is_slice = true;
|
||
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_len_index]->name = len_field_name;
|
||
entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize;
|
||
entry->data.structure.fields[slice_len_index]->src_index = slice_len_index;
|
||
entry->data.structure.fields[slice_len_index]->gen_index = 1;
|
||
|
||
entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]);
|
||
entry->data.structure.fields_by_name.put(len_field_name, entry->data.structure.fields[slice_len_index]);
|
||
|
||
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(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;
|
||
}
|
||
|
||
ZigType *child_type = ptr_type->data.pointer.child_type;
|
||
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)
|
||
{
|
||
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);
|
||
|
||
entry->size_in_bits = peer_slice_type->size_in_bits;
|
||
entry->abi_size = peer_slice_type->abi_size;
|
||
entry->abi_align = peer_slice_type->abi_align;
|
||
|
||
*parent_pointer = entry;
|
||
return entry;
|
||
}
|
||
|
||
if (type_has_bits(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 "undefined";
|
||
case CallingConventionC: return "ccc";
|
||
case CallingConventionCold: return "coldcc";
|
||
case CallingConventionNaked: return "nakedcc";
|
||
case CallingConventionStdcall: return "stdcallcc";
|
||
case CallingConventionAsync: return "async";
|
||
}
|
||
zig_unreachable();
|
||
}
|
||
|
||
static const char *calling_convention_fn_type_str(CallingConvention cc) {
|
||
switch (cc) {
|
||
case CallingConventionUnspecified: return "";
|
||
case CallingConventionC: return "extern ";
|
||
case CallingConventionCold: return "coldcc ";
|
||
case CallingConventionNaked: return "nakedcc ";
|
||
case CallingConventionStdcall: return "stdcallcc ";
|
||
case CallingConventionAsync: return "async ";
|
||
}
|
||
zig_unreachable();
|
||
}
|
||
|
||
bool calling_convention_allows_zig_types(CallingConvention cc) {
|
||
switch (cc) {
|
||
case CallingConventionUnspecified:
|
||
case CallingConventionAsync:
|
||
return true;
|
||
case CallingConventionC:
|
||
case CallingConventionCold:
|
||
case CallingConventionNaked:
|
||
case CallingConventionStdcall:
|
||
return false;
|
||
}
|
||
zig_unreachable();
|
||
}
|
||
|
||
ZigType *get_stack_trace_type(CodeGen *g) {
|
||
if (g->stack_trace_type == nullptr) {
|
||
ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace");
|
||
assert(stack_trace_type_val->type->id == ZigTypeIdMetaType);
|
||
|
||
g->stack_trace_type = stack_trace_type_val->data.x_type;
|
||
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(fn_type_id->return_type);
|
||
}
|
||
if (fn_type_id->cc != CallingConventionC) {
|
||
return false;
|
||
}
|
||
if (type_is_c_abi_int(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))
|
||
{
|
||
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_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);
|
||
const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
|
||
buf_appendf(&fn_type->name, "%s", cc_str);
|
||
buf_appendf(&fn_type->name, "fn(");
|
||
for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
|
||
FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
|
||
|
||
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);
|
||
}
|
||
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;
|
||
}
|
||
|
||
ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
|
||
Buf *type_name, UndefAllowed undef)
|
||
{
|
||
size_t backward_branch_count = 0;
|
||
size_t backward_branch_quota = default_backward_branch_quota;
|
||
return ir_eval_const_value(g, scope, node, type_entry,
|
||
&backward_branch_count, &backward_branch_quota,
|
||
nullptr, nullptr, node, type_name, nullptr, nullptr, undef);
|
||
}
|
||
|
||
Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type,
|
||
ConstExprValue *parent_type_val, bool *is_zero_bits)
|
||
{
|
||
Error err;
|
||
if (type_val->special != ConstValSpecialLazy) {
|
||
assert(type_val->special == ConstValSpecialStatic);
|
||
if ((type_val->data.x_type->id == ZigTypeIdStruct &&
|
||
type_val->data.x_type->data.structure.resolve_loop_flag_zero_bits) ||
|
||
(type_val->data.x_type->id == ZigTypeIdUnion &&
|
||
type_val->data.x_type->data.unionation.resolve_loop_flag_zero_bits) ||
|
||
type_val->data.x_type->id == ZigTypeIdPointer)
|
||
{
|
||
// Does a struct/union which contains a pointer field to itself have bits? Yes.
|
||
*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:
|
||
zig_unreachable();
|
||
case LazyValueIdPtrType: {
|
||
LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(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 LazyValueIdOptType:
|
||
case LazyValueIdSliceType:
|
||
case LazyValueIdErrUnionType:
|
||
*is_zero_bits = false;
|
||
return ErrorNone;
|
||
case LazyValueIdFnType: {
|
||
LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(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, ConstExprValue *type_val, bool *is_opaque_type) {
|
||
if (type_val->special != ConstValSpecialLazy) {
|
||
assert(type_val->special == ConstValSpecialStatic);
|
||
*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:
|
||
zig_unreachable();
|
||
case LazyValueIdSliceType:
|
||
case LazyValueIdPtrType:
|
||
case LazyValueIdFnType:
|
||
case LazyValueIdOptType:
|
||
case LazyValueIdErrUnionType:
|
||
*is_opaque_type = false;
|
||
return ErrorNone;
|
||
}
|
||
zig_unreachable();
|
||
}
|
||
|
||
static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue *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:
|
||
zig_unreachable();
|
||
case LazyValueIdSliceType: {
|
||
LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(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<LazyValuePtrType *>(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<LazyValueOptType *>(type_val->data.x_lazy);
|
||
return type_val_resolve_requires_comptime(g, &lazy_opt_type->payload_type->value);
|
||
}
|
||
case LazyValueIdFnType: {
|
||
LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(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<LazyValueErrUnionType *>(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, ConstExprValue *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:
|
||
zig_unreachable();
|
||
case LazyValueIdSliceType: {
|
||
LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(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<LazyValuePtrType *>(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:
|
||
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, ConstExprValue *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:
|
||
zig_unreachable();
|
||
case LazyValueIdSliceType:
|
||
case LazyValueIdPtrType:
|
||
case LazyValueIdFnType:
|
||
*abi_align = g->builtin_types.entry_usize->abi_align;
|
||
return ErrorNone;
|
||
case LazyValueIdOptType: {
|
||
LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);
|
||
return type_val_resolve_abi_align(g, &lazy_opt_type->payload_type->value, abi_align);
|
||
}
|
||
case LazyValueIdErrUnionType: {
|
||
LazyValueErrUnionType *lazy_err_union_type =
|
||
reinterpret_cast<LazyValueErrUnionType *>(type_val->data.x_lazy);
|
||
uint32_t payload_abi_align;
|
||
if ((err = type_val_resolve_abi_align(g, &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, ConstExprValue *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:
|
||
zig_unreachable();
|
||
case LazyValueIdSliceType: // it has the len field
|
||
case LazyValueIdOptType: // it has the optional bit
|
||
case LazyValueIdFnType:
|
||
return OnePossibleValueNo;
|
||
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<LazyValueErrUnionType *>(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) {
|
||
ConstExprValue *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);
|
||
const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
|
||
buf_appendf(&fn_type->name, "%s", cc_str);
|
||
buf_appendf(&fn_type->name, "fn(");
|
||
size_t i = 0;
|
||
for (; i < fn_type_id->next_param_index; i += 1) {
|
||
const char *comma_str = (i == 0) ? "" : ",";
|
||
buf_appendf(&fn_type->name, "%s%s", comma_str,
|
||
buf_ptr(&fn_type_id->param_info[i].type->name));
|
||
}
|
||
for (; i < fn_type_id->param_count; i += 1) {
|
||
const char *comma_str = (i == 0) ? "" : ",";
|
||
buf_appendf(&fn_type->name, "%svar", comma_str);
|
||
}
|
||
buf_appendf(&fn_type->name, ")var");
|
||
|
||
fn_type->data.fn.fn_type_id = *fn_type_id;
|
||
fn_type->data.fn.is_generic = true;
|
||
fn_type->abi_size = 0;
|
||
fn_type->size_in_bits = 0;
|
||
fn_type->abi_align = 0;
|
||
return fn_type;
|
||
}
|
||
|
||
void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc) {
|
||
assert(proto_node->type == NodeTypeFnProto);
|
||
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
||
|
||
if (fn_proto->cc == CallingConventionUnspecified) {
|
||
bool extern_abi = fn_proto->is_extern || fn_proto->is_export;
|
||
fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified;
|
||
} else {
|
||
fn_type_id->cc = fn_proto->cc;
|
||
}
|
||
|
||
fn_type_id->param_count = fn_proto->params.length;
|
||
fn_type_id->param_info = allocate<FnTypeParamInfo>(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) {
|
||
ConstExprValue *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);
|
||
ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr, UndefBad);
|
||
if (type_is_invalid(result_val->type))
|
||
return false;
|
||
|
||
ConstExprValue *ptr_field = result_val->data.x_struct.fields[slice_ptr_index];
|
||
ConstExprValue *len_field = result_val->data.x_struct.fields[slice_len_index];
|
||
|
||
assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
|
||
ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
|
||
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;
|
||
ConstExprValue *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 ZigTypeIdArgTuple:
|
||
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:
|
||
if (get_codegen_ptr_type(type_entry) != nullptr) {
|
||
return ErrorNone;
|
||
} else {
|
||
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 ZigTypeIdArgTuple:
|
||
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 = type_entry->data.fn.fn_type_id.cc == CallingConventionC ||
|
||
type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall;
|
||
return ErrorNone;
|
||
case ZigTypeIdPointer:
|
||
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
|
||
return err;
|
||
if (!type_has_bits(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(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, "@typeOf(%s).ReturnType.ErrorSet", 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) {
|
||
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, 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 if (calling_convention_allows_zig_types(fn_type_id.cc)) {
|
||
return get_generic_fn_type(g, &fn_type_id);
|
||
} else {
|
||
add_node_error(g, param_node,
|
||
buf_sprintf("var args not allowed in function with calling convention '%s'",
|
||
calling_convention_name(fn_type_id.cc)));
|
||
return g->builtin_types.entry_invalid;
|
||
}
|
||
} else if (param_node->data.param_decl.var_token != nullptr) {
|
||
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
|
||
add_node_error(g, param_node,
|
||
buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'",
|
||
calling_convention_name(fn_type_id.cc)));
|
||
return g->builtin_types.entry_invalid;
|
||
}
|
||
return get_generic_fn_type(g, &fn_type_id);
|
||
}
|
||
|
||
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(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;
|
||
}
|
||
}
|
||
|
||
switch (type_entry->id) {
|
||
case ZigTypeIdInvalid:
|
||
zig_unreachable();
|
||
case ZigTypeIdUnreachable:
|
||
case ZigTypeIdUndefined:
|
||
case ZigTypeIdNull:
|
||
case ZigTypeIdArgTuple:
|
||
case ZigTypeIdOpaque:
|
||
add_node_error(g, param_node->data.param_decl.type,
|
||
buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name)));
|
||
return g->builtin_types.entry_invalid;
|
||
case ZigTypeIdComptimeFloat:
|
||
case ZigTypeIdComptimeInt:
|
||
case ZigTypeIdEnumLiteral:
|
||
case ZigTypeIdBoundFn:
|
||
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 ZigTypeIdVector:
|
||
case ZigTypeIdFnFrame:
|
||
case ZigTypeIdAnyFrame:
|
||
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;
|
||
}
|
||
break;
|
||
}
|
||
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
|
||
param_info->type = type_entry;
|
||
param_info->is_noalias = param_node->data.param_decl.is_noalias;
|
||
}
|
||
|
||
if (fn_proto->align_expr != nullptr) {
|
||
if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &fn_type_id.alignment)) {
|
||
return g->builtin_types.entry_invalid;
|
||
}
|
||
fn_entry->align_bytes = fn_type_id.alignment;
|
||
}
|
||
|
||
if (fn_proto->return_var_token != nullptr) {
|
||
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
|
||
add_node_error(g, fn_proto->return_type,
|
||
buf_sprintf("return type 'var' not allowed in function with calling convention '%s'",
|
||
calling_convention_name(fn_type_id.cc)));
|
||
return g->builtin_types.entry_invalid;
|
||
}
|
||
add_node_error(g, proto_node,
|
||
buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
|
||
return g->builtin_types.entry_invalid;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
switch (specified_return_type->id) {
|
||
case ZigTypeIdInvalid:
|
||
zig_unreachable();
|
||
|
||
case ZigTypeIdUndefined:
|
||
case ZigTypeIdNull:
|
||
case ZigTypeIdArgTuple:
|
||
add_node_error(g, fn_proto->return_type,
|
||
buf_sprintf("return type '%s' not allowed", buf_ptr(&specified_return_type->name)));
|
||
return g->builtin_types.entry_invalid;
|
||
|
||
case ZigTypeIdOpaque:
|
||
{
|
||
ErrorMsg* msg = add_node_error(g, fn_proto->return_type,
|
||
buf_sprintf("opaque return type '%s' not allowed", 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("declared here"));
|
||
}
|
||
return g->builtin_types.entry_invalid;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
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 (fn_type_id.return_type->id) {
|
||
case ZigTypeIdInvalid:
|
||
case ZigTypeIdUndefined:
|
||
case ZigTypeIdNull:
|
||
case ZigTypeIdArgTuple:
|
||
case ZigTypeIdOpaque:
|
||
zig_unreachable();
|
||
|
||
case ZigTypeIdComptimeFloat:
|
||
case ZigTypeIdComptimeInt:
|
||
case ZigTypeIdEnumLiteral:
|
||
case ZigTypeIdBoundFn:
|
||
case ZigTypeIdMetaType:
|
||
case ZigTypeIdUnreachable:
|
||
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 ZigTypeIdVector:
|
||
case ZigTypeIdFnFrame:
|
||
case ZigTypeIdAnyFrame:
|
||
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;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return get_fn_type(g, &fn_type_id);
|
||
}
|
||
|
||
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;
|
||
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(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(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(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);
|
||
assert(decl_node->type == NodeTypeContainerDecl || decl_node->type == NodeTypeContainerInitExpr);
|
||
|
||
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 ? allocate<uint32_t>(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 err;
|
||
}
|
||
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 err;
|
||
}
|
||
|
||
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 = 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 err;
|
||
}
|
||
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->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(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;
|
||
|
||
|
||
assert(decl_node->type == NodeTypeContainerDecl);
|
||
|
||
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(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(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;
|
||
assert(decl_node->type == NodeTypeContainerDecl);
|
||
|
||
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;
|
||
|
||
assert(!enum_type->data.enumeration.fields);
|
||
uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
|
||
if (field_count == 0) {
|
||
add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
|
||
|
||
enum_type->data.enumeration.src_field_count = field_count;
|
||
enum_type->data.enumeration.fields = nullptr;
|
||
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
|
||
return ErrorSemanticAnalyzeFail;
|
||
}
|
||
|
||
enum_type->data.enumeration.src_field_count = field_count;
|
||
enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
|
||
enum_type->data.enumeration.fields_by_name.init(field_count);
|
||
|
||
Scope *scope = &enum_type->data.enumeration.decls_scope->base;
|
||
|
||
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
|
||
occupied_tag_values.init(field_count);
|
||
|
||
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;
|
||
|
||
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
|
||
ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
|
||
if (type_is_invalid(wanted_tag_int_type)) {
|
||
enum_type->data.enumeration.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 ErrorNone;
|
||
}
|
||
}
|
||
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);
|
||
|
||
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"));
|
||
}
|
||
|
||
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
|
||
ConstExprValue *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.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;
|
||
}
|
||
|
||
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;
|
||
|
||
occupied_tag_values.deinit();
|
||
|
||
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 (decl_node->type == NodeTypeContainerInitExpr) {
|
||
src_assert(struct_type->data.structure.is_inferred, decl_node);
|
||
src_assert(struct_type->data.structure.fields != nullptr, decl_node);
|
||
|
||
field_count = struct_type->data.structure.src_field_count;
|
||
} 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.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 (decl_node->type == NodeTypeContainerInitExpr) {
|
||
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;
|
||
}
|
||
|
||
ConstExprValue *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 (decl_node->type == NodeTypeContainerInitExpr) {
|
||
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->data.struct_field.type,
|
||
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;
|
||
|
||
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;
|
||
assert(decl_node->type == NodeTypeContainerDecl || decl_node->type == NodeTypeContainerInitExpr);
|
||
|
||
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 err;
|
||
}
|
||
} else if (packed) {
|
||
field->align = 1;
|
||
} else {
|
||
if ((err = type_val_resolve_abi_align(g, 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(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;
|
||
assert(decl_node->type == NodeTypeContainerDecl);
|
||
|
||
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;
|
||
|
||
assert(union_type->data.unionation.fields == nullptr);
|
||
uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
|
||
if (field_count == 0) {
|
||
add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
|
||
union_type->data.unionation.src_field_count = field_count;
|
||
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
|
||
return ErrorSemanticAnalyzeFail;
|
||
}
|
||
union_type->data.unionation.src_field_count = field_count;
|
||
union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
|
||
union_type->data.unionation.fields_by_name.init(field_count);
|
||
|
||
Scope *scope = &union_type->data.unionation.decls_scope->base;
|
||
|
||
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
|
||
|
||
AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr;
|
||
union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum ||
|
||
enum_type_node != nullptr;
|
||
bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
|
||
bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr) && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);
|
||
ZigType *tag_type;
|
||
bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety);
|
||
bool *covered_enum_fields;
|
||
ZigLLVMDIEnumerator **di_enumerators;
|
||
if (create_enum_type) {
|
||
occupied_tag_values.init(field_count);
|
||
|
||
di_enumerators = allocate<ZigLLVMDIEnumerator*>(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 = allocate<TypeEnumField>(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;
|
||
covered_enum_fields = allocate<bool>(enum_type->data.enumeration.src_field_count);
|
||
} else {
|
||
tag_type = nullptr;
|
||
}
|
||
union_type->data.unionation.tag_type = tag_type;
|
||
|
||
uint32_t gen_field_index = 0;
|
||
for (uint32_t i = 0; i < field_count; i += 1) {
|
||
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
|
||
Buf *field_name = field_node->data.struct_field.name;
|
||
TypeUnionField *union_field = &union_type->data.unionation.fields[i];
|
||
union_field->name = field_node->data.struct_field.name;
|
||
union_field->decl_node = field_node;
|
||
union_field->gen_index = UINT32_MAX;
|
||
|
||
auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
|
||
if (field_entry != nullptr) {
|
||
ErrorMsg *msg = add_node_error(g, field_node,
|
||
buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
|
||
add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
|
||
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
|
||
return ErrorSemanticAnalyzeFail;
|
||
}
|
||
|
||
bool field_is_zero_bits;
|
||
if (field_node->data.struct_field.type == nullptr) {
|
||
if (decl_node->data.container_decl.auto_enum ||
|
||
decl_node->data.container_decl.init_arg_expr != nullptr)
|
||
{
|
||
union_field->type_entry = g->builtin_types.entry_void;
|
||
field_is_zero_bits = true;
|
||
} else {
|
||
add_node_error(g, field_node, buf_sprintf("union field missing type"));
|
||
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
|
||
return ErrorSemanticAnalyzeFail;
|
||
}
|
||
} else {
|
||
ConstExprValue *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 (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
|
||
return ErrorSemanticAnalyzeFail;
|
||
|
||
bool field_is_opaque_type;
|
||
if ((err = type_val_resolve_is_opaque_type(g, 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, field_node->data.struct_field.type,
|
||
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, field_type_val)) {
|
||
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"));
|
||
}
|
||
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, field_type_val, union_type, nullptr, &field_is_zero_bits))) {
|
||
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
|
||
return ErrorSemanticAnalyzeFail;
|
||
}
|
||
}
|
||
|
||
if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {
|
||
ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
|
||
buf_create_from_str("untagged union field assignment"));
|
||
add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here"));
|
||
}
|
||
|
||
if (create_enum_type) {
|
||
di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i);
|
||
union_field->enum_field = &tag_type->data.enumeration.fields[i];
|
||
union_field->enum_field->name = field_name;
|
||
union_field->enum_field->decl_index = i;
|
||
union_field->enum_field->decl_node = field_node;
|
||
|
||
auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);
|
||
assert(prev_entry == nullptr); // caught by union de-duplicator above
|
||
|
||
AstNode *tag_value = field_node->data.struct_field.value;
|
||
// In this first pass we resolve explicit tag values.
|
||
// In a second pass we will fill in the unspecified ones.
|
||
if (tag_value != nullptr) {
|
||
ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type;
|
||
ConstExprValue *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 (enum_type_node != nullptr) {
|
||
union_field->enum_field = find_enum_type_field(tag_type, field_name);
|
||
if (union_field->enum_field == nullptr) {
|
||
ErrorMsg *msg = add_node_error(g, field_node,
|
||
buf_sprintf("enum field not found: '%s'", buf_ptr(field_name)));
|
||
add_error_note(g, msg, tag_type->data.enumeration.decl_node,
|
||
buf_sprintf("enum declared here"));
|
||
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
|
||
return ErrorSemanticAnalyzeFail;
|
||
}
|
||
covered_enum_fields[union_field->enum_field->decl_index] = true;
|
||
} else {
|
||
union_field->enum_field = allocate<TypeEnumField>(1);
|
||
union_field->enum_field->name = field_name;
|
||
union_field->enum_field->decl_index = i;
|
||
bigint_init_unsigned(&union_field->enum_field->value, i);
|
||
}
|
||
assert(union_field->enum_field != nullptr);
|
||
|
||
if (field_is_zero_bits)
|
||
continue;
|
||
|
||
union_field->gen_index = gen_field_index;
|
||
gen_field_index += 1;
|
||
}
|
||
|
||
bool src_have_tag = decl_node->data.container_decl.auto_enum ||
|
||
decl_node->data.container_decl.init_arg_expr != nullptr;
|
||
|
||
if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) {
|
||
const char *qual_str;
|
||
switch (union_type->data.unionation.layout) {
|
||
case ContainerLayoutAuto:
|
||
zig_unreachable();
|
||
case ContainerLayoutPacked:
|
||
qual_str = "packed";
|
||
break;
|
||
case ContainerLayoutExtern:
|
||
qual_str = "extern";
|
||
break;
|
||
}
|
||
AstNode *source_node = (decl_node->data.container_decl.init_arg_expr != nullptr) ?
|
||
decl_node->data.container_decl.init_arg_expr : decl_node;
|
||
add_node_error(g, source_node,
|
||
buf_sprintf("%s union does not support enum tag type", qual_str));
|
||
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
|
||
return ErrorSemanticAnalyzeFail;
|
||
}
|
||
|
||
if (create_enum_type) {
|
||
// Now iterate again and populate the unspecified tag values
|
||
uint32_t next_maybe_unoccupied_index = 0;
|
||
|
||
for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
|
||
AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
|
||
TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];
|
||
AstNode *tag_value = field_node->data.struct_field.value;
|
||
|
||
if (tag_value == nullptr) {
|
||
if (occupied_tag_values.size() == 0) {
|
||
bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index);
|
||
next_maybe_unoccupied_index += 1;
|
||
} else {
|
||
BigInt proposed_value;
|
||
for (;;) {
|
||
bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
|
||
next_maybe_unoccupied_index += 1;
|
||
auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
|
||
if (entry != nullptr) {
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
|
||
}
|
||
}
|
||
}
|
||
} else if (enum_type_node != nullptr) {
|
||
for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
|
||
TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
|
||
if (!covered_enum_fields[i]) {
|
||
AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
|
||
AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
|
||
ErrorMsg *msg = add_node_error(g, decl_node,
|
||
buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name)));
|
||
add_error_note(g, msg, field_node,
|
||
buf_sprintf("declared here"));
|
||
union_type->data.unionation.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<ScopeDecls *>(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);
|
||
}
|
||
}
|
||
|
||
ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
|
||
ZigFn *fn_entry = allocate<ZigFn>(1);
|
||
|
||
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;
|
||
}
|
||
|
||
static bool scope_is_root_decls(Scope *scope) {
|
||
while (scope) {
|
||
if (scope->id == ScopeIdDecls) {
|
||
ScopeDecls *scope_decls = (ScopeDecls *)scope;
|
||
return is_top_level_struct(scope_decls->container_type);
|
||
}
|
||
scope = scope->parent;
|
||
}
|
||
zig_unreachable();
|
||
}
|
||
|
||
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, bool ccc) {
|
||
if (ccc) {
|
||
if (strcmp(symbol_name, "main") == 0 && g->libc_link_lib != nullptr) {
|
||
g->have_c_main = true;
|
||
} else if (strcmp(symbol_name, "WinMain") == 0 &&
|
||
g->zig_target->os == OsWindows)
|
||
{
|
||
g->have_winmain = true;
|
||
} else if (strcmp(symbol_name, "WinMainCRTStartup") == 0 &&
|
||
g->zig_target->os == OsWindows)
|
||
{
|
||
g->have_winmain_crt_startup = true;
|
||
} else if (strcmp(symbol_name, "DllMainCRTStartup") == 0 &&
|
||
g->zig_target->os == OsWindows)
|
||
{
|
||
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) {
|
||
ZigType *import = tld_fn->base.import;
|
||
AstNode *source_node = tld_fn->base.source_node;
|
||
if (source_node->type == NodeTypeFnProto) {
|
||
AstNodeFnProto *fn_proto = &source_node->data.fn_proto;
|
||
|
||
AstNode *fn_def_node = fn_proto->fn_def_node;
|
||
|
||
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 (fn_proto->is_export) {
|
||
bool ccc = (fn_proto->cc == CallingConventionUnspecified || fn_proto->cc == CallingConventionC);
|
||
add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), GlobalLinkageIdStrong, ccc);
|
||
}
|
||
|
||
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_prototypes.put_unique(tld_fn->base.name, &tld_fn->base);
|
||
}
|
||
|
||
Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
|
||
|
||
fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry);
|
||
|
||
if (fn_proto->section_expr != nullptr) {
|
||
if (!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;
|
||
}
|
||
}
|
||
|
||
if (fn_table_entry->type_entry->id == ZigTypeIdInvalid) {
|
||
tld_fn->base.resolution = TldResolutionInvalid;
|
||
return;
|
||
}
|
||
|
||
if (!fn_table_entry->type_entry->data.fn.is_generic) {
|
||
if (fn_def_node)
|
||
g->fn_defs.append(fn_table_entry);
|
||
}
|
||
|
||
// if 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_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
|
||
fn_table_entry->inferred_async_node = fn_table_entry->proto_node;
|
||
}
|
||
|
||
if (scope_is_root_decls(tld_fn->base.parent_scope) && import == g->root_import) {
|
||
if (g->have_pub_main && buf_eql_str(tld_fn->base.name, "main")) {
|
||
g->main_fn = fn_table_entry;
|
||
}
|
||
}
|
||
} 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 (!is_export && !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;
|
||
}
|
||
} 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;
|
||
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->root_package)
|
||
return;
|
||
|
||
Buf *decl_name_buf = node->data.test_decl.name;
|
||
|
||
Buf *test_name = g->test_name_prefix ?
|
||
buf_sprintf("%s%s", buf_ptr(g->test_name_prefix), buf_ptr(decl_name_buf)) : decl_name_buf;
|
||
|
||
if (g->test_filter != nullptr && strstr(buf_ptr(test_name), buf_ptr(g->test_filter)) == nullptr) {
|
||
return;
|
||
}
|
||
|
||
TldFn *tld_fn = allocate<TldFn>(1);
|
||
init_tld(&tld_fn->base, TldIdFn, test_name, VisibModPrivate, node, &decls_scope->base);
|
||
g->resolve_queue.append(&tld_fn->base);
|
||
}
|
||
|
||
static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
|
||
assert(node->type == NodeTypeCompTime);
|
||
|
||
TldCompTime *tld_comptime = allocate<TldCompTime>(1);
|
||
init_tld(&tld_comptime->base, TldIdCompTime, nullptr, VisibModPrivate, node, &decls_scope->base);
|
||
g->resolve_queue.append(&tld_comptime->base);
|
||
}
|
||
|
||
void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node,
|
||
Scope *parent_scope)
|
||
{
|
||
tld->id = id;
|
||
tld->name = name;
|
||
tld->visib_mod = visib_mod;
|
||
tld->source_node = source_node;
|
||
tld->import = source_node ? source_node->owner : nullptr;
|
||
tld->parent_scope = parent_scope;
|
||
}
|
||
|
||
void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) {
|
||
Tld *tld = get_container_scope(g->compile_var_import)->decl_table.get(name);
|
||
resolve_top_level_decl(g, tld, tld->source_node, false);
|
||
assert(tld->id == TldIdVar);
|
||
TldVar *tld_var = (TldVar *)tld;
|
||
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 = allocate<TldVar>(1);
|
||
init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base);
|
||
tld_var->extern_lib_name = node->data.variable_declaration.lib_name;
|
||
add_top_level_decl(g, decls_scope, &tld_var->base);
|
||
break;
|
||
}
|
||
case NodeTypeFnProto:
|
||
{
|
||
// if the name is missing, we immediately announce an error
|
||
Buf *fn_name = node->data.fn_proto.name;
|
||
if (fn_name == nullptr) {
|
||
add_node_error(g, node, buf_sprintf("missing function name"));
|
||
break;
|
||
}
|
||
|
||
VisibMod visib_mod = node->data.fn_proto.visib_mod;
|
||
TldFn *tld_fn = allocate<TldFn>(1);
|
||
init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base);
|
||
tld_fn->extern_lib_name = node->data.fn_proto.lib_name;
|
||
add_top_level_decl(g, decls_scope, &tld_fn->base);
|
||
|
||
break;
|
||
}
|
||
case NodeTypeUsingNamespace: {
|
||
VisibMod visib_mod = node->data.using_namespace.visib_mod;
|
||
TldUsingNamespace *tld_using_namespace = allocate<TldUsingNamespace>(1);
|
||
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 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:
|
||
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, AstNode *source_node, ZigType *type_entry) {
|
||
switch (type_entry->id) {
|
||
case ZigTypeIdInvalid:
|
||
return g->builtin_types.entry_invalid;
|
||
case ZigTypeIdUnreachable:
|
||
case ZigTypeIdUndefined:
|
||
case ZigTypeIdNull:
|
||
case ZigTypeIdArgTuple:
|
||
case ZigTypeIdOpaque:
|
||
add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed",
|
||
buf_ptr(&type_entry->name)));
|
||
return g->builtin_types.entry_invalid;
|
||
case 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, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type)
|
||
{
|
||
Error err;
|
||
assert(const_value != nullptr);
|
||
assert(var_type != nullptr);
|
||
|
||
ZigVar *variable_entry = allocate<ZigVar>(1);
|
||
variable_entry->const_value = const_value;
|
||
variable_entry->var_type = var_type;
|
||
variable_entry->parent_scope = parent_scope;
|
||
variable_entry->shadowable = false;
|
||
variable_entry->mem_slot_index = SIZE_MAX;
|
||
variable_entry->src_arg_index = SIZE_MAX;
|
||
|
||
assert(name);
|
||
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) {
|
||
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 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->type, proposed_type);
|
||
}
|
||
}
|
||
|
||
assert(!is_export || !is_extern);
|
||
|
||
ConstExprValue *init_value = nullptr;
|
||
|
||
// TODO more validation for types that can't be used for export/extern variables
|
||
ZigType *implicit_type = nullptr;
|
||
if (explicit_type && explicit_type->id == ZigTypeIdInvalid) {
|
||
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;
|
||
}
|
||
|
||
ZigType *type = explicit_type ? explicit_type : implicit_type;
|
||
assert(type != nullptr); // should have been caught by the parser
|
||
|
||
ConstExprValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type);
|
||
|
||
tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
|
||
is_const, init_val, &tld_var->base, 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->section_name)) {
|
||
tld_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) {
|
||
add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong);
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
ConstExprValue *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);
|
||
ConstExprValue *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_instruction->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_instruction->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);
|
||
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.is_slice;
|
||
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 ZigTypeIdArgTuple:
|
||
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;
|
||
}
|
||
|
||
ZigType *get_codegen_ptr_type(ZigType *type) {
|
||
ZigType *ty = get_src_ptr_type(type);
|
||
if (ty == nullptr || !type_has_bits(ty))
|
||
return nullptr;
|
||
return ty;
|
||
}
|
||
|
||
bool type_is_nonnull_ptr(ZigType *type) {
|
||
return get_codegen_ptr_type(type) == type && !ptr_allows_addr_zero(type);
|
||
}
|
||
|
||
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 = 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 `?extern fn() 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(ZigType *type) {
|
||
ZigType *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 && get_codegen_ptr_type(param_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(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(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 (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 == CallModifierNoAsync)
|
||
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) {
|
||
IrInstructionCallGen *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.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) {
|
||
IrInstructionAwaitGen *await = fn->await_list.at(i);
|
||
// TODO If this is a noasync await, it doesn't count
|
||
// https://github.com/ziglang/zig/issues/3157
|
||
switch (analyze_callee_async(g, fn, await->target_fn, await->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;
|
||
|
||
ZigType *block_return_type = ir_analyze(g, &fn->ir_executable,
|
||
&fn->analyzed_executable, fn_type_id->return_type, return_type_node);
|
||
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)
|
||
{
|
||
ZigType *inferred_err_set_type;
|
||
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;
|
||
} else {
|
||
add_node_error(g, return_type_node,
|
||
buf_sprintf("function with inferred error set must return at least one possible error"));
|
||
fn->anal_state = FnAnalStateInvalid;
|
||
return;
|
||
}
|
||
|
||
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 = allocate<ErrorTableEntry *>(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];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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(g, stderr, &fn->analyzed_executable, 4, IrPassGen);
|
||
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);
|
||
|
||
ir_gen_fn(g, fn_table_entry);
|
||
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(g, stderr, &fn_table_entry->ir_executable, 4, IrPassSrc);
|
||
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 = allocate<RootStruct>(1);
|
||
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);
|
||
}
|
||
|
||
if (source_kind == SourceKindRoot) {
|
||
// Look for main
|
||
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);
|
||
|
||
if (top_level_decl->type == NodeTypeFnDef) {
|
||
AstNode *proto_node = top_level_decl->data.fn_def.fn_proto;
|
||
assert(proto_node->type == NodeTypeFnProto);
|
||
Buf *proto_name = proto_node->data.fn_proto.name;
|
||
|
||
bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub);
|
||
if (is_pub) {
|
||
if (buf_eql_str(proto_name, "main")) {
|
||
g->have_pub_main = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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 = allocate<TldContainer>(1);
|
||
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;
|
||
}
|
||
|
||
bool is_valid_vector_elem_type(ZigType *elem_type) {
|
||
return elem_type->id == ZigTypeIdInt ||
|
||
elem_type->id == ZigTypeIdFloat ||
|
||
elem_type->id == ZigTypeIdBool ||
|
||
get_codegen_ptr_type(elem_type) != nullptr;
|
||
}
|
||
|
||
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
|
||
assert(is_valid_vector_elem_type(elem_type));
|
||
|
||
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(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;
|
||
|
||
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(ZigType *type_entry) {
|
||
switch (type_entry->id) {
|
||
case ZigTypeIdInvalid:
|
||
case ZigTypeIdMetaType:
|
||
case ZigTypeIdComptimeFloat:
|
||
case ZigTypeIdComptimeInt:
|
||
case ZigTypeIdEnumLiteral:
|
||
case ZigTypeIdUndefined:
|
||
case ZigTypeIdNull:
|
||
case ZigTypeIdBoundFn:
|
||
case ZigTypeIdArgTuple:
|
||
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(type_entry);
|
||
case ZigTypeIdErrorUnion:
|
||
return type_has_bits(type_entry->data.error_union.payload_type);
|
||
case ZigTypeIdOptional:
|
||
return type_has_bits(type_entry->data.maybe.child_type) &&
|
||
!type_is_nonnull_ptr(type_entry->data.maybe.child_type) &&
|
||
type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet;
|
||
case ZigTypeIdUnion:
|
||
return type_has_bits(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(ConstExprValue *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(ConstExprValue *const_val) {
|
||
uint32_t hash_val = 0;
|
||
switch (const_val->data.x_ptr.mut) {
|
||
case ConstPtrMutRuntimeVar:
|
||
hash_val += (uint32_t)3500721036;
|
||
break;
|
||
case ConstPtrMutComptimeConst:
|
||
hash_val += (uint32_t)4214318515;
|
||
break;
|
||
case 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);
|
||
hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
|
||
return hash_val;
|
||
case ConstPtrSpecialBaseStruct:
|
||
hash_val += (uint32_t)3518317043;
|
||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
|
||
hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
|
||
return hash_val;
|
||
case 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(ConstExprValue *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) * 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 ZigTypeIdArgTuple:
|
||
return (uint32_t)const_val->data.x_arg_tuple.start_index * (uint32_t)281907309 +
|
||
(uint32_t)const_val->data.x_arg_tuple.end_index * (uint32_t)2290442768;
|
||
case 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_codegen_ptr_type(const_val->type) != nullptr) {
|
||
return hash_const_val_ptr(const_val) * 1992916303;
|
||
} else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) {
|
||
return hash_const_val_error_set(const_val) * 3147031929;
|
||
} else {
|
||
if (const_val->data.x_optional) {
|
||
return hash_const_val(const_val->data.x_optional) * 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:
|
||
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) {
|
||
ConstExprValue *generic_param = &id->params[i];
|
||
if (generic_param->special != ConstValSpecialRuntime) {
|
||
result += hash_const_val(generic_param);
|
||
result += hash_ptr(generic_param->type);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
|
||
assert(a->fn_entry);
|
||
if (a->fn_entry != b->fn_entry) return false;
|
||
if (a->param_count != b->param_count) return false;
|
||
for (size_t i = 0; i < a->param_count; i += 1) {
|
||
ConstExprValue *a_val = &a->params[i];
|
||
ConstExprValue *b_val = &b->params[i];
|
||
if (a_val->type != b_val->type) return false;
|
||
if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) {
|
||
assert(a_val->special == ConstValSpecialStatic);
|
||
assert(b_val->special == ConstValSpecialStatic);
|
||
if (!const_values_equal(a->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(ConstExprValue *value) {
|
||
assert(value != nullptr);
|
||
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->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_codegen_ptr_type(value->type) != nullptr)
|
||
return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
|
||
if (value->data.x_optional == nullptr)
|
||
return false;
|
||
return can_mutate_comptime_var_state(value->data.x_optional);
|
||
|
||
case 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);
|
||
|
||
case ZigTypeIdArgTuple:
|
||
zig_panic("TODO var args at comptime is currently not supported");
|
||
}
|
||
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);
|
||
|
||
case ZigTypeIdArgTuple:
|
||
zig_panic("TODO var args at comptime is currently not supported");
|
||
}
|
||
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(ZigType *type_entry) {
|
||
assert(type_entry != nullptr);
|
||
assert(!type_is_invalid(type_entry));
|
||
assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
|
||
return type_entry->abi_size != 0;
|
||
}
|
||
|
||
// 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 ZigTypeIdArgTuple:
|
||
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:
|
||
for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
|
||
TypeStructField *field = type_entry->data.structure.fields[i];
|
||
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:
|
||
return OnePossibleValueInvalid;
|
||
case OnePossibleValueNo:
|
||
return OnePossibleValueNo;
|
||
case OnePossibleValueYes:
|
||
continue;
|
||
}
|
||
}
|
||
return OnePossibleValueYes;
|
||
case ZigTypeIdErrorSet:
|
||
case ZigTypeIdEnum:
|
||
case ZigTypeIdInt:
|
||
case ZigTypeIdVector:
|
||
return type_has_bits(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();
|
||
}
|
||
|
||
ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
|
||
Error err;
|
||
switch (ty->id) {
|
||
case ZigTypeIdInvalid:
|
||
zig_unreachable();
|
||
case ZigTypeIdComptimeFloat:
|
||
case ZigTypeIdComptimeInt:
|
||
case ZigTypeIdEnumLiteral:
|
||
case ZigTypeIdUndefined:
|
||
case ZigTypeIdNull:
|
||
case ZigTypeIdMetaType:
|
||
case ZigTypeIdBoundFn:
|
||
case ZigTypeIdArgTuple:
|
||
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, ConstExprValue *const_val, Buf *str) {
|
||
auto entry = g->string_literals_table.maybe_get(str);
|
||
if (entry != nullptr) {
|
||
memcpy(const_val, entry->value, sizeof(ConstExprValue));
|
||
return;
|
||
}
|
||
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str));
|
||
const_val->data.x_array.special = ConstArraySpecialBuf;
|
||
const_val->data.x_array.data.s_buf = str;
|
||
|
||
g->string_literals_table.put(str, const_val);
|
||
}
|
||
|
||
ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_str_lit(g, const_val, str);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
|
||
// first we build the underlying array
|
||
size_t len_with_null = buf_len(str) + 1;
|
||
ConstExprValue *array_val = create_const_vals(1);
|
||
array_val->special = ConstValSpecialStatic;
|
||
array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null);
|
||
// TODO buf optimization
|
||
array_val->data.x_array.data.s_none.elements = create_const_vals(len_with_null);
|
||
for (size_t i = 0; i < buf_len(str); i += 1) {
|
||
ConstExprValue *this_char = &array_val->data.x_array.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(str)[i]);
|
||
}
|
||
ConstExprValue *null_char = &array_val->data.x_array.data.s_none.elements[len_with_null - 1];
|
||
null_char->special = ConstValSpecialStatic;
|
||
null_char->type = g->builtin_types.entry_u8;
|
||
bigint_init_unsigned(&null_char->data.x_bigint, 0);
|
||
|
||
// then make the pointer point to it
|
||
const_val->special = ConstValSpecialStatic;
|
||
// TODO make this `[*]null u8` instead of `[*]u8`
|
||
const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
|
||
PtrLenUnknown, 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 = 0;
|
||
const_val->data.x_ptr.data.base_array.is_cstr = true;
|
||
}
|
||
ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_c_str_lit(g, const_val, str);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_bigint(ConstExprValue *const_val, ZigType *type, const BigInt *bigint) {
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = type;
|
||
bigint_init_bigint(&const_val->data.x_bigint, bigint);
|
||
}
|
||
|
||
ConstExprValue *create_const_bigint(ZigType *type, const BigInt *bigint) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_bigint(const_val, type, bigint);
|
||
return const_val;
|
||
}
|
||
|
||
|
||
void init_const_unsigned_negative(ConstExprValue *const_val, 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;
|
||
}
|
||
|
||
ConstExprValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_unsigned_negative(const_val, type, x, negative);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_usize(CodeGen *g, ConstExprValue *const_val, uint64_t x) {
|
||
return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false);
|
||
}
|
||
|
||
ConstExprValue *create_const_usize(CodeGen *g, uint64_t x) {
|
||
return create_const_unsigned_negative(g->builtin_types.entry_usize, x, false);
|
||
}
|
||
|
||
void init_const_signed(ConstExprValue *const_val, ZigType *type, int64_t x) {
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = type;
|
||
bigint_init_signed(&const_val->data.x_bigint, x);
|
||
}
|
||
|
||
ConstExprValue *create_const_signed(ZigType *type, int64_t x) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_signed(const_val, type, x);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_float(ConstExprValue *const_val, 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();
|
||
}
|
||
}
|
||
|
||
ConstExprValue *create_const_float(ZigType *type, double value) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_float(const_val, type, value);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_enum(ConstExprValue *const_val, ZigType *type, const BigInt *tag) {
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = type;
|
||
bigint_init_bigint(&const_val->data.x_enum_tag, tag);
|
||
}
|
||
|
||
ConstExprValue *create_const_enum(ZigType *type, const BigInt *tag) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_enum(const_val, type, tag);
|
||
return const_val;
|
||
}
|
||
|
||
|
||
void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value) {
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = g->builtin_types.entry_bool;
|
||
const_val->data.x_bool = value;
|
||
}
|
||
|
||
ConstExprValue *create_const_bool(CodeGen *g, bool value) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_bool(g, const_val, value);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_runtime(ConstExprValue *const_val, ZigType *type) {
|
||
const_val->special = ConstValSpecialRuntime;
|
||
const_val->type = type;
|
||
}
|
||
|
||
ConstExprValue *create_const_runtime(ZigType *type) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_runtime(const_val, type);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_type(CodeGen *g, ConstExprValue *const_val, ZigType *type_value) {
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = g->builtin_types.entry_type;
|
||
const_val->data.x_type = type_value;
|
||
}
|
||
|
||
ConstExprValue *create_const_type(CodeGen *g, ZigType *type_value) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_type(g, const_val, type_value);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
|
||
size_t start, size_t len, bool is_const)
|
||
{
|
||
assert(array_val->type->id == 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(2);
|
||
|
||
init_const_ptr_array(g, const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const,
|
||
PtrLenUnknown);
|
||
init_const_usize(g, const_val->data.x_struct.fields[slice_len_index], len);
|
||
}
|
||
|
||
ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t start, size_t len, bool is_const) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_slice(g, const_val, array_val, start, len, is_const);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
|
||
size_t elem_index, bool is_const, PtrLen ptr_len)
|
||
{
|
||
assert(array_val->type->id == 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;
|
||
}
|
||
|
||
ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const,
|
||
PtrLen ptr_len)
|
||
{
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_ptr_ref(CodeGen *g, ConstExprValue *const_val, ConstExprValue *pointee_val, bool is_const) {
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = get_pointer_to_type(g, pointee_val->type, is_const);
|
||
const_val->data.x_ptr.special = ConstPtrSpecialRef;
|
||
const_val->data.x_ptr.data.ref.pointee = pointee_val;
|
||
}
|
||
|
||
ConstExprValue *create_const_ptr_ref(CodeGen *g, ConstExprValue *pointee_val, bool is_const) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_ptr_ref(g, const_val, pointee_val, is_const);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_ptr_hard_coded_addr(CodeGen *g, ConstExprValue *const_val, 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;
|
||
}
|
||
|
||
ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type,
|
||
size_t addr, bool is_const)
|
||
{
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const);
|
||
return const_val;
|
||
}
|
||
|
||
void init_const_arg_tuple(CodeGen *g, ConstExprValue *const_val, size_t arg_index_start, size_t arg_index_end) {
|
||
const_val->special = ConstValSpecialStatic;
|
||
const_val->type = g->builtin_types.entry_arg_tuple;
|
||
const_val->data.x_arg_tuple.start_index = arg_index_start;
|
||
const_val->data.x_arg_tuple.end_index = arg_index_end;
|
||
}
|
||
|
||
ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_t arg_index_end) {
|
||
ConstExprValue *const_val = create_const_vals(1);
|
||
init_const_arg_tuple(g, const_val, arg_index_start, arg_index_end);
|
||
return const_val;
|
||
}
|
||
|
||
|
||
ConstExprValue *create_const_vals(size_t count) {
|
||
ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count, "ConstGlobalRefs");
|
||
ConstExprValue *vals = allocate<ConstExprValue>(count, "ConstExprValue");
|
||
for (size_t i = 0; i < count; i += 1) {
|
||
vals[i].global_refs = &global_refs[i];
|
||
}
|
||
return vals;
|
||
}
|
||
|
||
ConstExprValue **alloc_const_vals_ptrs(size_t count) {
|
||
return realloc_const_vals_ptrs(nullptr, 0, count);
|
||
}
|
||
|
||
ConstExprValue **realloc_const_vals_ptrs(ConstExprValue **ptr, size_t old_count, size_t new_count) {
|
||
assert(new_count >= old_count);
|
||
|
||
size_t new_item_count = new_count - old_count;
|
||
ConstExprValue **result = reallocate(ptr, old_count, new_count, "ConstExprValue*");
|
||
ConstExprValue *vals = create_const_vals(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 = reallocate(ptr, old_count, new_count, "TypeStructField*");
|
||
TypeStructField *vals = allocate<TypeStructField>(new_item_count, "TypeStructField");
|
||
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 = allocate_nonzero<ZigType>(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<ScopeExpr *>(scope) : nullptr;
|
||
bool looking_for_exprs = true;
|
||
for (;;) {
|
||
scope = scope->parent;
|
||
switch (scope->id) {
|
||
case ScopeIdDeferExpr:
|
||
case ScopeIdDecls:
|
||
case ScopeIdFnDef:
|
||
case ScopeIdCompTime:
|
||
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<ScopeLoop *>(scope);
|
||
if (loop_scope->spill_scope != nullptr) {
|
||
loop_scope->spill_scope->need_spill = MemoizedBoolTrue;
|
||
}
|
||
looking_for_exprs = false;
|
||
continue;
|
||
}
|
||
case ScopeIdExpr: {
|
||
if (!looking_for_exprs) {
|
||
// Now we're only looking for a block, to see if it's in a loop (see the case ScopeIdBlock)
|
||
continue;
|
||
}
|
||
ScopeExpr *parent_expr_scope = reinterpret_cast<ScopeExpr *>(scope);
|
||
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 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<SrcField> 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), 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) {
|
||
IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);
|
||
alloca_gen->base.id = IrInstructionIdAllocaGen;
|
||
alloca_gen->base.source_node = fn->proto_node;
|
||
alloca_gen->base.scope = fn->child_scope;
|
||
alloca_gen->base.value.type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false);
|
||
alloca_gen->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.
|
||
IrInstruction *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) {
|
||
IrInstructionCallGen *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) {
|
||
add_node_error(g, call->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.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.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.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) {
|
||
IrInstructionAwaitGen *await = fn->await_list.at(i);
|
||
// TODO If this is a noasync await, it doesn't suspend
|
||
// https://github.com/ziglang/zig/issues/3157
|
||
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.scope);
|
||
|
||
if (await->result_loc != nullptr) {
|
||
// If there's a result location, that is the spill
|
||
continue;
|
||
}
|
||
if (await->base.ref_count == 0)
|
||
continue;
|
||
if (!type_has_bits(await->base.value.type))
|
||
continue;
|
||
await->result_loc = ir_create_alloca(g, await->base.scope, await->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) {
|
||
IrBasicBlock *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) {
|
||
IrInstruction *instruction = block->instruction_list.at(instr_i);
|
||
if (instruction->id == IrInstructionIdSuspendFinish) {
|
||
mark_suspension_point(instruction->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) {
|
||
IrBasicBlock *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) {
|
||
IrInstruction *instruction = block->instruction_list.at(instr_i);
|
||
if (instruction->id == IrInstructionIdAwaitGen ||
|
||
instruction->id == IrInstructionIdVarPtr ||
|
||
instruction->id == IrInstructionIdDeclRef ||
|
||
instruction->id == IrInstructionIdAllocaGen)
|
||
{
|
||
// This instruction does its own spilling specially, or otherwise doesn't need it.
|
||
continue;
|
||
}
|
||
if (instruction->value.special != ConstValSpecialRuntime)
|
||
continue;
|
||
if (instruction->ref_count == 0)
|
||
continue;
|
||
if ((err = type_resolve(g, instruction->value.type, ResolveStatusZeroBitsKnown)))
|
||
return ErrorSemanticAnalyzeFail;
|
||
if (!type_has_bits(instruction->value.type))
|
||
continue;
|
||
if (scope_needs_spill(instruction->scope)) {
|
||
instruction->spill = ir_create_alloca(g, instruction->scope, instruction->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<SrcField> 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 = 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), 0});
|
||
}
|
||
|
||
for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) {
|
||
IrInstructionAllocaGen *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 = ptr_type->data.pointer.child_type;
|
||
if (!type_has_bits(child_type))
|
||
continue;
|
||
if (instruction->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.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 = ty->data.pointer.child_type;
|
||
|
||
if ((err = type_resolve(g, elem_type, ResolveStatusZeroBitsKnown)))
|
||
return err;
|
||
|
||
if (type_has_bits(elem_type)) {
|
||
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) {
|
||
// The is_comptime field can be left null, which means not comptime.
|
||
if (var->is_comptime == nullptr)
|
||
return false;
|
||
// 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);
|
||
return var->is_comptime->child->value.data.x_bool;
|
||
}
|
||
// 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->value.type->id == ZigTypeIdBool);
|
||
return var->is_comptime->value.data.x_bool;
|
||
}
|
||
|
||
bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
|
||
if (a->data.x_ptr.special != b->data.x_ptr.special)
|
||
return false;
|
||
if (a->data.x_ptr.mut != b->data.x_ptr.mut)
|
||
return false;
|
||
switch (a->data.x_ptr.special) {
|
||
case ConstPtrSpecialInvalid:
|
||
zig_unreachable();
|
||
case ConstPtrSpecialRef:
|
||
if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
|
||
return false;
|
||
return true;
|
||
case ConstPtrSpecialBaseArray:
|
||
if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
|
||
a->data.x_ptr.data.base_array.array_val->global_refs !=
|
||
b->data.x_ptr.data.base_array.array_val->global_refs)
|
||
{
|
||
return false;
|
||
}
|
||
if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
|
||
return false;
|
||
if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
|
||
return false;
|
||
return true;
|
||
case ConstPtrSpecialBaseStruct:
|
||
if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
|
||
a->data.x_ptr.data.base_struct.struct_val->global_refs !=
|
||
b->data.x_ptr.data.base_struct.struct_val->global_refs)
|
||
{
|
||
return false;
|
||
}
|
||
if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
|
||
return false;
|
||
return true;
|
||
case 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 &&
|
||
a->data.x_ptr.data.base_err_union_code.err_union_val->global_refs !=
|
||
b->data.x_ptr.data.base_err_union_code.err_union_val->global_refs)
|
||
{
|
||
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 &&
|
||
a->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs !=
|
||
b->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs)
|
||
{
|
||
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 &&
|
||
a->data.x_ptr.data.base_optional_payload.optional_val->global_refs !=
|
||
b->data.x_ptr.data.base_optional_payload.optional_val->global_refs)
|
||
{
|
||
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, ConstExprValue *a, ConstExprValue *b, size_t len) {
|
||
assert(a->data.x_array.special != ConstArraySpecialUndef);
|
||
assert(b->data.x_array.special != ConstArraySpecialUndef);
|
||
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);
|
||
|
||
ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
|
||
ConstExprValue *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, ConstExprValue *a, ConstExprValue *b) {
|
||
assert(a->type->id == b->type->id);
|
||
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(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) {
|
||
ConstExprValue *field_a = a->data.x_struct.fields[i];
|
||
ConstExprValue *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_codegen_ptr_type(a->type) != nullptr)
|
||
return const_values_equal_ptr(a, b);
|
||
if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) {
|
||
return (a->data.x_optional == nullptr && b->data.x_optional == nullptr);
|
||
} else {
|
||
return const_values_equal(g, a->data.x_optional, b->data.x_optional);
|
||
}
|
||
case ZigTypeIdErrorUnion:
|
||
zig_panic("TODO");
|
||
case ZigTypeIdArgTuple:
|
||
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
|
||
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
|
||
case 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, ConstExprValue *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, ConstExprValue *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:
|
||
if (const_val->data.x_ptr.data.base_array.is_cstr) {
|
||
buf_appendf(buf, "*(c str lit)");
|
||
return;
|
||
} else {
|
||
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, ConstExprValue *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, ConstExprValue *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: {
|
||
ConstExprValue *base = &array->data.s_none.elements[start];
|
||
assert(start + len <= const_val->type->data.array.len);
|
||
|
||
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, ConstExprValue *const_val) {
|
||
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_codegen_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)) {
|
||
ConstExprValue *len_val = const_val->data.x_struct.fields[slice_len_index];
|
||
size_t len = bigint_as_usize(&len_val->data.x_bigint);
|
||
|
||
ConstExprValue *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);
|
||
ConstExprValue *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);
|
||
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));
|
||
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 ZigTypeIdArgTuple:
|
||
{
|
||
buf_appendf(buf, "(args value)");
|
||
return;
|
||
}
|
||
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 ZigTypeIdArgTuple:
|
||
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) +
|
||
((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +
|
||
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
|
||
(x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
|
||
(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);
|
||
case ZigTypeIdArray:
|
||
return hash_ptr(x.data.array.child_type) +
|
||
((uint32_t)x.data.array.size ^ (uint32_t)2122979968);
|
||
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 ZigTypeIdArgTuple:
|
||
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.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;
|
||
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, ConstExprValue *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(field_count);
|
||
for (size_t i = 0; i < field_count; i += 1) {
|
||
ConstExprValue *field_val = const_val->data.x_struct.fields[i];
|
||
field_val->type = 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, ConstExprValue *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, ConstExprValue *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 = create_const_vals(elem_count);
|
||
for (size_t i = 0; i < elem_count; i += 1) {
|
||
ConstExprValue *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 = create_const_vals(elem_count);
|
||
for (size_t i = 0; i < elem_count; i += 1) {
|
||
ConstExprValue *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,
|
||
ZigTypeIdArgTuple,
|
||
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.is_slice)
|
||
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 ZigTypeIdArgTuple:
|
||
return 20;
|
||
case ZigTypeIdOpaque:
|
||
return 21;
|
||
case ZigTypeIdFnFrame:
|
||
return 22;
|
||
case ZigTypeIdAnyFrame:
|
||
return 23;
|
||
case ZigTypeIdVector:
|
||
return 24;
|
||
case ZigTypeIdEnumLiteral:
|
||
return 25;
|
||
}
|
||
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 ZigTypeIdArgTuple:
|
||
return "ArgTuple";
|
||
case ZigTypeIdOpaque:
|
||
return "Opaque";
|
||
case ZigTypeIdVector:
|
||
return "Vector";
|
||
case ZigTypeIdFnFrame:
|
||
return "Frame";
|
||
case ZigTypeIdAnyFrame:
|
||
return "AnyFrame";
|
||
}
|
||
zig_unreachable();
|
||
}
|
||
|
||
LinkLib *create_link_lib(Buf *name) {
|
||
LinkLib *link_lib = allocate<LinkLib>(1);
|
||
link_lib->name = name;
|
||
return link_lib;
|
||
}
|
||
|
||
LinkLib *add_link_lib(CodeGen *g, Buf *name) {
|
||
bool is_libc = buf_eql_str(name, "c");
|
||
|
||
if (is_libc && g->libc_link_lib != nullptr)
|
||
return g->libc_link_lib;
|
||
|
||
for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
|
||
LinkLib *existing_lib = g->link_libs_list.at(i);
|
||
if (buf_eql_buf(existing_lib->name, name)) {
|
||
return existing_lib;
|
||
}
|
||
}
|
||
|
||
LinkLib *link_lib = create_link_lib(name);
|
||
g->link_libs_list.append(link_lib);
|
||
|
||
if (is_libc)
|
||
g->libc_link_lib = link_lib;
|
||
|
||
return link_lib;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
|
||
Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name));
|
||
resolve_top_level_decl(codegen, tld, nullptr, false);
|
||
assert(tld->id == TldIdVar);
|
||
TldVar *tld_var = (TldVar *)tld;
|
||
ConstExprValue *var_value = tld_var->var->const_value;
|
||
assert(var_value != nullptr);
|
||
return var_value;
|
||
}
|
||
|
||
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) {
|
||
if (g->enable_cache) {
|
||
return cache_add_file_fetch(&g->cache_hash, resolved_path, contents);
|
||
} else {
|
||
return os_fetch_file_path(resolved_path, contents);
|
||
}
|
||
}
|
||
|
||
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) {
|
||
const size_t ty_size = type_size(g, ty);
|
||
if (get_codegen_ptr_type(ty) != 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
|
||
bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
|
||
return (ty->id == ZigTypeIdInt ||
|
||
ty->id == ZigTypeIdFloat ||
|
||
ty->id == ZigTypeIdBool ||
|
||
ty->id == ZigTypeIdEnum ||
|
||
ty->id == ZigTypeIdVoid ||
|
||
ty->id == ZigTypeIdUnreachable ||
|
||
get_codegen_ptr_type(ty) != nullptr);
|
||
}
|
||
|
||
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,
|
||
ConstExprValue *const_val, ZigType *wanted_type)
|
||
{
|
||
ConstExprValue 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)
|
||
{
|
||
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)
|
||
{
|
||
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(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(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 = allocate<LLVMTypeRef>(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(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) {
|
||
ZigType *field_type = struct_type->data.structure.fields[i]->type_entry;
|
||
if (!type_has_bits(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 (!type_has_bits(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(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(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 = allocate<ZigLLVMDIType*>(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];
|
||
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<DIType>(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(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(enum_type->data.enumeration.fields);
|
||
ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(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 = tag_int_type->size_in_bits;
|
||
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 = allocate<ZigLLVMDIType*>(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(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 = decl_node->data.container_decl.fields.at(i);
|
||
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(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);
|
||
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);
|
||
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(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)
|
||
{
|
||
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);
|
||
} 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);
|
||
}
|
||
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(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(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(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;
|
||
}
|
||
|
||
LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type);
|
||
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_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_llvm_type);
|
||
uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_llvm_type);
|
||
uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
|
||
|
||
uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_llvm_type);
|
||
uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_llvm_type);
|
||
uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1);
|
||
|
||
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[] = {
|
||
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
|
||
"val", di_file, line,
|
||
val_debug_size_in_bits,
|
||
val_debug_align_in_bits,
|
||
val_offset_in_bits,
|
||
ZigLLVM_DIFlags_Zero, child_llvm_di_type),
|
||
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
|
||
"maybe", di_file, line,
|
||
maybe_debug_size_in_bits,
|
||
maybe_debug_align_in_bits,
|
||
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, 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.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(payload_type)) {
|
||
assert(type_has_bits(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(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(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;
|
||
|
||
// TODO https://github.com/ziglang/zig/issues/1424
|
||
type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)type->data.array.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)type->data.array.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 = fn_type_id->cc == CallingConventionC;
|
||
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<LLVMTypeRef> 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<ZigLLVMDIType *> param_di_types = {};
|
||
ZigType *gen_return_type;
|
||
if (is_async) {
|
||
gen_return_type = g->builtin_types.entry_void;
|
||
param_di_types.append(get_llvm_di_type(g, gen_return_type));
|
||
} else if (!type_has_bits(fn_type_id->return_type)) {
|
||
gen_return_type = g->builtin_types.entry_void;
|
||
param_di_types.append(get_llvm_di_type(g, gen_return_type));
|
||
} else if (first_arg_return) {
|
||
gen_return_type = g->builtin_types.entry_void;
|
||
param_di_types.append(get_llvm_di_type(g, gen_return_type));
|
||
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 = allocate<FnGenParamInfo>(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 = allocate<FnGenParamInfo>(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(type_entry))
|
||
continue;
|
||
|
||
ZigType *gen_type;
|
||
if (handle_is_ptr(type_entry)) {
|
||
gen_type = get_pointer_to_type(g, type_entry, true);
|
||
gen_param_info->is_byval = true;
|
||
} else {
|
||
gen_type = type_entry;
|
||
}
|
||
gen_param_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);
|
||
fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
|
||
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<ZigLLVMDIType *> param_di_types = {};
|
||
ZigList<LLVMTypeRef> gen_param_types = {};
|
||
// first "parameter" is return value
|
||
param_di_types.append(get_llvm_di_type(g, gen_return_type));
|
||
|
||
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<ZigLLVMDIEnumerator *> 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);
|
||
LLVMTypeRef ptr_fn_llvm_type = LLVMPointerType(fn_type, 0);
|
||
if (result_type == nullptr) {
|
||
g->anyframe_fn_type = ptr_fn_llvm_type;
|
||
}
|
||
|
||
ZigList<LLVMTypeRef> field_types = {};
|
||
ZigList<ZigLLVMDIType *> 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(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:
|
||
case ZigTypeIdArgTuple:
|
||
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.is_slice)
|
||
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, type->size_in_bits,
|
||
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(bool ok, AstNode *source_node) {
|
||
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);
|
||
}
|
||
const char *msg = "assertion failed. This is a bug in the Zig compiler.";
|
||
stage2_panic(msg, strlen(msg));
|
||
}
|
||
|
||
IrInstruction *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn,
|
||
ZigType *var_type, const char *name_hint)
|
||
{
|
||
IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);
|
||
alloca_gen->base.id = IrInstructionIdAllocaGen;
|
||
alloca_gen->base.source_node = source_node;
|
||
alloca_gen->base.scope = scope;
|
||
alloca_gen->base.value.type = get_pointer_to_type(g, var_type, false);
|
||
alloca_gen->base.ref_count = 1;
|
||
alloca_gen->name_hint = name_hint;
|
||
fn->alloca_gen_list.append(alloca_gen);
|
||
return &alloca_gen->base;
|
||
}
|
||
|
||
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;
|
||
}
|