zig/src/analyze.cpp
2017-03-10 02:29:01 -05:00

4087 lines
160 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 "config.h"
#include "error.hpp"
#include "ir.hpp"
#include "ir_print.hpp"
#include "os.hpp"
#include "parser.hpp"
#include "zig_llvm.hpp"
static const size_t default_backward_branch_quota = 1000;
static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type);
static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type);
static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type);
static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type);
static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
// if this assert fails, then parseh generated code that
// failed semantic analysis, which isn't supposed to happen
assert(!node->owner->c_import_node);
ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
node->owner->source_code, node->owner->line_offsets, msg);
g->errors.append(err);
return err;
}
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
// if this assert fails, then parseh generated code that
// failed semantic analysis, which isn't supposed to happen
assert(!node->owner->c_import_node);
ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
node->owner->source_code, node->owner->line_offsets, msg);
err_msg_add_note(parent_msg, err);
return err;
}
TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
entry->id = id;
return entry;
}
static ScopeDecls **get_container_scope_ptr(TypeTableEntry *type_entry) {
if (type_entry->id == TypeTableEntryIdStruct) {
return &type_entry->data.structure.decls_scope;
} else if (type_entry->id == TypeTableEntryIdEnum) {
return &type_entry->data.enumeration.decls_scope;
} else if (type_entry->id == TypeTableEntryIdUnion) {
return &type_entry->data.unionation.decls_scope;
}
zig_unreachable();
}
ScopeDecls *get_container_scope(TypeTableEntry *type_entry) {
return *get_container_scope_ptr(type_entry);
}
void init_scope(Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) {
dest->id = id;
dest->source_node = source_node;
dest->parent = parent;
}
ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import) {
assert(node == nullptr || node->type == NodeTypeRoot || node->type == NodeTypeContainerDecl || node->type == NodeTypeFnCallExpr);
ScopeDecls *scope = allocate<ScopeDecls>(1);
init_scope(&scope->base, ScopeIdDecls, node, parent);
scope->decl_table.init(4);
scope->container_type = container_type;
scope->import = import;
return scope;
}
ScopeBlock *create_block_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeBlock);
ScopeBlock *scope = allocate<ScopeBlock>(1);
init_scope(&scope->base, ScopeIdBlock, node, parent);
scope->label_table.init(1);
return scope;
}
ScopeDefer *create_defer_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeDefer);
ScopeDefer *scope = allocate<ScopeDefer>(1);
init_scope(&scope->base, ScopeIdDefer, node, parent);
return scope;
}
ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeDefer);
ScopeDeferExpr *scope = allocate<ScopeDeferExpr>(1);
init_scope(&scope->base, ScopeIdDeferExpr, node, parent);
return scope;
}
Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) {
ScopeVarDecl *scope = allocate<ScopeVarDecl>(1);
init_scope(&scope->base, ScopeIdVarDecl, node, parent);
scope->var = var;
return &scope->base;
}
ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeFnCallExpr);
ScopeCImport *scope = allocate<ScopeCImport>(1);
init_scope(&scope->base, ScopeIdCImport, node, parent);
buf_resize(&scope->buf, 0);
return scope;
}
Scope *create_loop_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr);
ScopeLoop *scope = allocate<ScopeLoop>(1);
init_scope(&scope->base, ScopeIdLoop, node, parent);
return &scope->base;
}
ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
assert(!node || node->type == NodeTypeFnDef);
ScopeFnDef *scope = allocate<ScopeFnDef>(1);
init_scope(&scope->base, ScopeIdFnDef, node, parent);
scope->fn_entry = fn_entry;
return scope;
}
Scope *create_comptime_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeCompTime || node->type == NodeTypeSwitchExpr);
ScopeCompTime *scope = allocate<ScopeCompTime>(1);
init_scope(&scope->base, ScopeIdCompTime, node, parent);
return &scope->base;
}
ImportTableEntry *get_scope_import(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
assert(decls_scope->import);
return decls_scope->import;
}
scope = scope->parent;
}
zig_unreachable();
}
static TypeTableEntry *new_container_type_entry(TypeTableEntryId id, AstNode *source_node, Scope *parent_scope) {
TypeTableEntry *entry = new_type_table_entry(id);
*get_container_scope_ptr(entry) = create_decls_scope(source_node, parent_scope, entry, get_scope_import(parent_scope));
return entry;
}
// TODO no reason to limit to 8/16/32/64
static size_t bits_needed_for_unsigned(uint64_t x) {
if (x <= UINT8_MAX) {
return 8;
} else if (x <= UINT16_MAX) {
return 16;
} else if (x <= UINT32_MAX) {
return 32;
} else {
return 64;
}
}
bool type_is_complete(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
zig_unreachable();
case TypeTableEntryIdStruct:
return type_entry->data.structure.complete;
case TypeTableEntryIdEnum:
return type_entry->data.enumeration.complete;
case TypeTableEntryIdUnion:
return type_entry->data.unionation.complete;
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
return true;
}
zig_unreachable();
}
bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
zig_unreachable();
case TypeTableEntryIdStruct:
return type_entry->data.structure.zero_bits_known;
case TypeTableEntryIdEnum:
return type_entry->data.enumeration.zero_bits_known;
case TypeTableEntryIdUnion:
return type_entry->data.unionation.zero_bits_known;
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
return true;
}
zig_unreachable();
}
uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) {
assert(type_is_complete(type_entry));
TypeTableEntry *canon_type = get_underlying_type(type_entry);
if (!type_has_bits(type_entry))
return 0;
if (canon_type->id == TypeTableEntryIdStruct && canon_type->data.structure.layout == ContainerLayoutPacked) {
uint64_t size_in_bits = type_size_bits(g, type_entry);
return (size_in_bits + 7) / 8;
} else if (canon_type->id == TypeTableEntryIdArray) {
TypeTableEntry *canon_child_type = get_underlying_type(canon_type->data.array.child_type);
if (canon_child_type->id == TypeTableEntryIdStruct &&
canon_child_type->data.structure.layout == ContainerLayoutPacked)
{
uint64_t size_in_bits = type_size_bits(g, type_entry);
return (size_in_bits + 7) / 8;
}
}
return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
}
uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {
assert(type_is_complete(type_entry));
TypeTableEntry *canon_type = get_underlying_type(type_entry);
if (!type_has_bits(type_entry))
return 0;
if (canon_type->id == TypeTableEntryIdStruct && canon_type->data.structure.layout == ContainerLayoutPacked) {
uint64_t result = 0;
for (size_t i = 0; i < canon_type->data.structure.src_field_count; i += 1) {
result += type_size_bits(g, canon_type->data.structure.fields[i].type_entry);
}
return result;
} else if (canon_type->id == TypeTableEntryIdArray) {
TypeTableEntry *canon_child_type = get_underlying_type(canon_type->data.array.child_type);
if (canon_child_type->id == TypeTableEntryIdStruct &&
canon_child_type->data.structure.layout == ContainerLayoutPacked)
{
return canon_type->data.array.len * type_size_bits(g, canon_child_type);
}
}
return LLVMSizeOfTypeInBits(g->target_data_ref, canon_type->type_ref);
}
static bool is_slice(TypeTableEntry *type) {
return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice;
}
TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
return get_int_type(g, false, bits_needed_for_unsigned(x));
}
TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
bool is_volatile, uint32_t bit_offset, uint32_t unaligned_bit_count)
{
assert(child_type->id != TypeTableEntryIdInvalid);
TypeId type_id = {};
TypeTableEntry **parent_pointer = nullptr;
if (unaligned_bit_count != 0 || is_volatile) {
type_id.id = TypeTableEntryIdPointer;
type_id.data.pointer.child_type = child_type;
type_id.data.pointer.is_const = is_const;
type_id.data.pointer.is_volatile = is_volatile;
type_id.data.pointer.bit_offset = bit_offset;
type_id.data.pointer.unaligned_bit_count = unaligned_bit_count;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry)
return existing_entry->value;
} else {
assert(bit_offset == 0);
parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)];
if (*parent_pointer)
return *parent_pointer;
}
type_ensure_zero_bits_known(g, child_type);
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
const char *const_str = is_const ? "const " : "";
const char *volatile_str = is_volatile ? "volatile " : "";
buf_resize(&entry->name, 0);
if (unaligned_bit_count == 0) {
buf_appendf(&entry->name, "&%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name));
} else {
buf_appendf(&entry->name, "&:%" PRIu32 ":%" PRIu32 " %s%s%s", bit_offset,
bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name));
}
TypeTableEntry *canon_child_type = get_underlying_type(child_type);
assert(canon_child_type->id != TypeTableEntryIdInvalid);
entry->zero_bits = !type_has_bits(canon_child_type);
if (!entry->zero_bits) {
entry->type_ref = LLVMPointerType(child_type->type_ref, 0);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
assert(child_type->di_type);
entry->di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, child_type->di_type,
debug_size_in_bits, debug_align_in_bits, buf_ptr(&entry->name));
} else {
entry->di_type = g->builtin_types.entry_void->di_type;
}
entry->data.pointer.child_type = child_type;
entry->data.pointer.is_const = is_const;
entry->data.pointer.is_volatile = is_volatile;
entry->data.pointer.bit_offset = bit_offset;
entry->data.pointer.unaligned_bit_count = unaligned_bit_count;
if (parent_pointer) {
*parent_pointer = entry;
} else {
g->type_table.put(type_id, entry);
}
return entry;
}
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
return get_pointer_to_type_extra(g, child_type, is_const, false, 0, 0);
}
TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
if (child_type->maybe_parent) {
TypeTableEntry *entry = child_type->maybe_parent;
return entry;
} else {
ensure_complete_type(g, child_type);
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe);
assert(child_type->type_ref);
assert(child_type->di_type);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
if (child_type->id == TypeTableEntryIdPointer ||
child_type->id == TypeTableEntryIdFn)
{
// this is an optimization but also is necessary for calling C
// functions where all pointers are maybe pointers
// function types are technically pointers
entry->type_ref = child_type->type_ref;
entry->di_type = child_type->di_type;
} else {
// create a struct with a boolean whether this is the null value
LLVMTypeRef elem_types[] = {
child_type->type_ref,
LLVMInt1Type(),
};
entry->type_ref = LLVMStructType(elem_types, 2, false);
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name),
compile_unit_scope, di_file, line);
uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref);
uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref);
uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0);
TypeTableEntry *bool_type = g->builtin_types.entry_bool;
uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_type->type_ref);
uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_type->type_ref);
uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"val", di_file, line,
val_debug_size_in_bits,
val_debug_align_in_bits,
val_offset_in_bits,
0, child_type->di_type),
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"maybe", di_file, line,
maybe_debug_size_in_bits,
maybe_debug_align_in_bits,
maybe_offset_in_bits,
0, child_type->di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&entry->name),
di_file, line, debug_size_in_bits, debug_align_in_bits, 0,
nullptr, di_element_types, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
entry->di_type = replacement_di_type;
}
entry->data.maybe.child_type = child_type;
child_type->maybe_parent = entry;
return entry;
}
}
TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
if (child_type->error_parent)
return child_type->error_parent;
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
assert(child_type->type_ref);
assert(child_type->di_type);
ensure_complete_type(g, child_type);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "%%%s", buf_ptr(&child_type->name));
entry->data.error.child_type = child_type;
if (!type_has_bits(child_type)) {
entry->type_ref = g->err_tag_type->type_ref;
entry->di_type = g->err_tag_type->di_type;
} else {
LLVMTypeRef elem_types[] = {
g->err_tag_type->type_ref,
child_type->type_ref,
};
entry->type_ref = LLVMStructType(elem_types, 2, false);
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name),
compile_unit_scope, di_file, line);
uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref);
uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, g->err_tag_type->type_ref);
uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_err_index);
uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref);
uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref);
uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref,
err_union_payload_index);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"tag", di_file, line,
tag_debug_size_in_bits,
tag_debug_align_in_bits,
tag_offset_in_bits,
0, child_type->di_type),
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"value", di_file, line,
value_debug_size_in_bits,
value_debug_align_in_bits,
value_offset_in_bits,
0, child_type->di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&entry->name),
di_file, line,
debug_size_in_bits,
debug_align_in_bits,
0,
nullptr, di_element_types, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
entry->di_type = replacement_di_type;
}
child_type->error_parent = entry;
return entry;
}
TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) {
TypeId type_id = {};
type_id.id = TypeTableEntryIdArray;
type_id.data.array.child_type = child_type;
type_id.data.array.size = array_size;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry) {
TypeTableEntry *entry = existing_entry->value;
return entry;
}
ensure_complete_type(g, child_type);
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
entry->zero_bits = (array_size == 0) || child_type->zero_bits;
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name));
if (!entry->zero_bits) {
entry->type_ref = child_type->type_ref ? LLVMArrayType(child_type->type_ref, array_size) : nullptr;
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
entry->di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, debug_size_in_bits,
debug_align_in_bits, child_type->di_type, array_size);
}
entry->data.array.child_type = child_type;
entry->data.array.len = array_size;
g->type_table.put(type_id, entry);
return entry;
}
static void slice_type_common_init(CodeGen *g, TypeTableEntry *child_type,
bool is_const, TypeTableEntry *entry)
{
TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const);
unsigned element_count = 2;
entry->data.structure.layout = ContainerLayoutAuto;
entry->data.structure.is_slice = true;
entry->data.structure.src_field_count = element_count;
entry->data.structure.gen_field_count = element_count;
entry->data.structure.fields = allocate<TypeStructField>(element_count);
entry->data.structure.fields[slice_ptr_index].name = buf_create_from_str("ptr");
entry->data.structure.fields[slice_ptr_index].type_entry = pointer_type;
entry->data.structure.fields[slice_ptr_index].src_index = slice_ptr_index;
entry->data.structure.fields[slice_ptr_index].gen_index = 0;
entry->data.structure.fields[slice_len_index].name = buf_create_from_str("len");
entry->data.structure.fields[slice_len_index].type_entry = g->builtin_types.entry_usize;
entry->data.structure.fields[slice_len_index].src_index = slice_len_index;
entry->data.structure.fields[slice_len_index].gen_index = 1;
assert(type_has_zero_bits_known(child_type));
if (child_type->zero_bits) {
entry->data.structure.gen_field_count = 1;
entry->data.structure.fields[slice_ptr_index].gen_index = SIZE_MAX;
entry->data.structure.fields[slice_len_index].gen_index = 0;
}
}
TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
assert(child_type->id != TypeTableEntryIdInvalid);
TypeTableEntry **parent_pointer = &child_type->slice_parent[(is_const ? 1 : 0)];
if (*parent_pointer) {
return *parent_pointer;
} else if (is_const) {
TypeTableEntry *var_peer = get_slice_type(g, child_type, false);
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name));
slice_type_common_init(g, child_type, is_const, entry);
entry->type_ref = var_peer->type_ref;
entry->di_type = var_peer->di_type;
entry->data.structure.complete = true;
entry->data.structure.zero_bits_known = true;
*parent_pointer = entry;
return entry;
} else {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
// If the child type is []const T then we need to make sure the type ref
// and debug info is the same as if the child type were []T.
if (is_slice(child_type)) {
TypeTableEntry *ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
if (ptr_type->data.pointer.is_const) {
TypeTableEntry *non_const_child_type = get_slice_type(g,
ptr_type->data.pointer.child_type, false);
TypeTableEntry *var_peer = get_slice_type(g, non_const_child_type, false);
entry->type_ref = var_peer->type_ref;
entry->di_type = var_peer->di_type;
}
}
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name));
slice_type_common_init(g, child_type, is_const, entry);
if (!entry->type_ref) {
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name));
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name),
compile_unit_scope, di_file, line);
if (child_type->zero_bits) {
LLVMTypeRef element_types[] = {
g->builtin_types.entry_usize->type_ref,
};
LLVMStructSetBody(entry->type_ref, element_types, 1, false);
TypeTableEntry *usize_type = g->builtin_types.entry_usize;
uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref);
uint64_t len_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, usize_type->type_ref);
uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"len", di_file, line,
len_debug_size_in_bits,
len_debug_align_in_bits,
len_offset_in_bits,
0, usize_type->di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&entry->name),
di_file, line, debug_size_in_bits, debug_align_in_bits, 0,
nullptr, di_element_types, 1, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
entry->di_type = replacement_di_type;
} else {
TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const);
unsigned element_count = 2;
LLVMTypeRef element_types[] = {
pointer_type->type_ref,
g->builtin_types.entry_usize->type_ref,
};
LLVMStructSetBody(entry->type_ref, element_types, element_count, false);
uint64_t ptr_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, pointer_type->type_ref);
uint64_t ptr_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, pointer_type->type_ref);
uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0);
TypeTableEntry *usize_type = g->builtin_types.entry_usize;
uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref);
uint64_t len_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, usize_type->type_ref);
uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"ptr", di_file, line,
ptr_debug_size_in_bits,
ptr_debug_align_in_bits,
ptr_offset_in_bits,
0, pointer_type->di_type),
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"len", di_file, line,
len_debug_size_in_bits,
len_debug_align_in_bits,
len_offset_in_bits,
0, usize_type->di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&entry->name),
di_file, line, debug_size_in_bits, debug_align_in_bits, 0,
nullptr, di_element_types, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
entry->di_type = replacement_di_type;
}
}
entry->data.structure.complete = true;
entry->data.structure.zero_bits_known = true;
*parent_pointer = entry;
return entry;
}
}
TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdTypeDecl);
buf_init_from_str(&entry->name, name);
entry->type_ref = child_type->type_ref;
entry->di_type = child_type->di_type;
entry->zero_bits = child_type->zero_bits;
entry->data.type_decl.child_type = child_type;
entry->data.type_decl.canonical_type = get_underlying_type(child_type);
return entry;
}
TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry) {
TypeTableEntry *fn_type = fn_entry->type_entry;
assert(fn_type->id == TypeTableEntryIdFn);
if (fn_type->data.fn.bound_fn_parent)
return fn_type->data.fn.bound_fn_parent;
TypeTableEntry *bound_fn_type = new_type_table_entry(TypeTableEntryIdBoundFn);
bound_fn_type->data.bound_fn.fn_type = fn_type;
bound_fn_type->zero_bits = true;
buf_resize(&bound_fn_type->name, 0);
buf_appendf(&bound_fn_type->name, "(bound %s)", buf_ptr(&fn_type->name));
fn_type->data.fn.bound_fn_parent = bound_fn_type;
return bound_fn_type;
}
TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
if (table_entry) {
return table_entry->value;
}
ensure_complete_type(g, fn_type_id->return_type);
TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
fn_type->data.fn.fn_type_id = *fn_type_id;
if (fn_type_id->is_cold) {
// cold calling convention only works on x86.
// but we can add the cold attribute later.
if (g->zig_target.arch.arch == ZigLLVM_x86 ||
g->zig_target.arch.arch == ZigLLVM_x86_64)
{
fn_type->data.fn.calling_convention = LLVMColdCallConv;
} else {
fn_type->data.fn.calling_convention = LLVMFastCallConv;
}
} else if (fn_type_id->is_extern) {
fn_type->data.fn.calling_convention = LLVMCCallConv;
} else {
fn_type->data.fn.calling_convention = LLVMFastCallConv;
}
bool skip_debug_info = false;
// populate the name of the type
buf_resize(&fn_type->name, 0);
const char *extern_str = fn_type_id->is_extern ? "extern " : "";
const char *naked_str = fn_type_id->is_naked ? "nakedcc " : "";
const char *cold_str = fn_type_id->is_cold ? "coldcc " : "";
buf_appendf(&fn_type->name, "%s%s%sfn(", extern_str, naked_str, cold_str);
for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
TypeTableEntry *param_type = param_info->type;
const char *comma = (i == 0) ? "" : ", ";
const char *noalias_str = param_info->is_noalias ? "noalias " : "";
buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(&param_type->name));
skip_debug_info = skip_debug_info || !param_type->di_type;
}
if (fn_type_id->is_var_args) {
const char *comma = (fn_type_id->param_count == 0) ? "" : ", ";
buf_appendf(&fn_type->name, "%s...", comma);
}
buf_appendf(&fn_type->name, ")");
if (fn_type_id->return_type->id != TypeTableEntryIdVoid) {
buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name));
}
skip_debug_info = skip_debug_info || !fn_type_id->return_type->di_type;
// next, loop over the parameters again and compute debug information
// and codegen information
if (!skip_debug_info) {
bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type);
// +1 for maybe making the first argument the return value
LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);
// +1 because 0 is the return type and +1 for maybe making first arg ret val
ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(2 + fn_type_id->param_count);
param_di_types[0] = fn_type_id->return_type->di_type;
size_t gen_param_index = 0;
TypeTableEntry *gen_return_type;
if (!type_has_bits(fn_type_id->return_type)) {
gen_return_type = g->builtin_types.entry_void;
} else if (first_arg_return) {
TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
gen_param_types[gen_param_index] = gen_type->type_ref;
gen_param_index += 1;
// after the gen_param_index += 1 because 0 is the return type
param_di_types[gen_param_index] = gen_type->di_type;
gen_return_type = g->builtin_types.entry_void;
} else {
gen_return_type = fn_type_id->return_type;
}
fn_type->data.fn.gen_return_type = gen_return_type;
fn_type->data.fn.gen_param_info = allocate<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];
TypeTableEntry *type_entry = src_param_info->type;
FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i];
gen_param_info->src_index = i;
gen_param_info->gen_index = SIZE_MAX;
ensure_complete_type(g, type_entry);
if (type_has_bits(type_entry)) {
TypeTableEntry *gen_type;
if (handle_is_ptr(type_entry)) {
gen_type = get_pointer_to_type(g, type_entry, true);
gen_param_info->is_byval = true;
} else {
gen_type = type_entry;
}
gen_param_types[gen_param_index] = gen_type->type_ref;
gen_param_info->gen_index = gen_param_index;
gen_param_info->type = gen_type;
gen_param_index += 1;
// after the gen_param_index += 1 because 0 is the return type
param_di_types[gen_param_index] = gen_type->di_type;
}
}
fn_type->data.fn.gen_param_count = gen_param_index;
fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
gen_param_types, gen_param_index, fn_type_id->is_var_args);
fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types, gen_param_index + 1, 0);
}
g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);
return fn_type;
}
static TypeTableEntryId container_to_type(ContainerKind kind) {
switch (kind) {
case ContainerKindStruct:
return TypeTableEntryIdStruct;
case ContainerKindEnum:
return TypeTableEntryIdEnum;
case ContainerKindUnion:
return TypeTableEntryIdUnion;
}
zig_unreachable();
}
TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
AstNode *decl_node, const char *name, ContainerLayout layout)
{
TypeTableEntryId type_id = container_to_type(kind);
TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, scope);
switch (kind) {
case ContainerKindStruct:
entry->data.structure.decl_node = decl_node;
entry->data.structure.layout = layout;
break;
case ContainerKindEnum:
entry->data.enumeration.decl_node = decl_node;
entry->data.enumeration.layout = layout;
break;
case ContainerKindUnion:
entry->data.unionation.decl_node = decl_node;
entry->data.unionation.layout = layout;
break;
}
unsigned line = decl_node ? decl_node->line : 0;
ImportTableEntry *import = get_scope_import(scope);
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name);
entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), name,
ZigLLVMFileToScope(import->di_file), import->di_file, line + 1);
buf_init_from_str(&entry->name, name);
return entry;
}
TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry) {
if (type_entry->id == TypeTableEntryIdTypeDecl) {
return type_entry->data.type_decl.canonical_type;
} else {
return type_entry;
}
}
static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, TypeTableEntry *type_entry, Buf *type_name) {
size_t backward_branch_count = 0;
return ir_eval_const_value(g, scope, node, type_entry,
&backward_branch_count, default_backward_branch_quota,
nullptr, nullptr, node, type_name, nullptr);
}
TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr);
if (result->value.type->id == TypeTableEntryIdInvalid)
return g->builtin_types.entry_invalid;
assert(result->value.special != ConstValSpecialRuntime);
return result->value.data.x_type;
}
static TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
buf_init_from_str(&fn_type->name, "fn(");
size_t i = 0;
for (; i < fn_type_id->next_param_index; i += 1) {
const char *comma_str = (i == 0) ? "" : ",";
buf_appendf(&fn_type->name, "%s%s", comma_str,
buf_ptr(&fn_type_id->param_info[i].type->name));
}
for (; i < fn_type_id->param_count; i += 1) {
const char *comma_str = (i == 0) ? "" : ",";
buf_appendf(&fn_type->name, "%svar", comma_str);
}
buf_appendf(&fn_type->name, ")->var");
fn_type->data.fn.fn_type_id = *fn_type_id;
fn_type->data.fn.is_generic = true;
fn_type->zero_bits = true;
return fn_type;
}
void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
fn_type_id->is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport);
fn_type_id->is_naked = fn_proto->is_nakedcc;
fn_type_id->is_cold = fn_proto->is_coldcc;
fn_type_id->param_count = fn_proto->params.length;
fn_type_id->param_info = allocate_nonzero<FnTypeParamInfo>(param_count_alloc);
fn_type_id->next_param_index = 0;
fn_type_id->is_var_args = fn_proto->is_var_args;
}
static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index);
assert(param_node->type == NodeTypeParamDecl);
bool param_is_inline = param_node->data.param_decl.is_inline;
bool param_is_var_args = param_node->data.param_decl.is_var_args;
if (param_is_inline) {
if (fn_type_id.is_extern) {
add_node_error(g, param_node,
buf_sprintf("comptime parameter not allowed in extern function"));
return g->builtin_types.entry_invalid;
}
return get_generic_fn_type(g, &fn_type_id);
} else if (param_is_var_args) {
if (fn_type_id.is_extern) {
fn_type_id.param_count = fn_type_id.next_param_index;
continue;
} else {
return get_generic_fn_type(g, &fn_type_id);
}
}
TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
return g->builtin_types.entry_invalid;
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdArgTuple:
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name)));
return g->builtin_types.entry_invalid;
case TypeTableEntryIdVar:
if (fn_type_id.is_extern) {
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type 'var' not allowed in extern function"));
return g->builtin_types.entry_invalid;
}
return get_generic_fn_type(g, &fn_type_id);
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdMetaType:
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' must be declared inline",
buf_ptr(&type_entry->name)));
return g->builtin_types.entry_invalid;
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdEnumTag:
break;
}
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
param_info->type = type_entry;
param_info->is_noalias = param_node->data.param_decl.is_noalias;
}
fn_type_id.return_type = analyze_type_expr(g, child_scope, fn_proto->return_type);
switch (fn_type_id.return_type->id) {
case TypeTableEntryIdInvalid:
return g->builtin_types.entry_invalid;
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdArgTuple:
add_node_error(g, fn_proto->return_type,
buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name)));
return g->builtin_types.entry_invalid;
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdVar:
case TypeTableEntryIdMetaType:
if (fn_type_id.is_extern) {
add_node_error(g, fn_proto->return_type,
buf_sprintf("return type '%s' not allowed in extern function",
buf_ptr(&fn_type_id.return_type->name)));
return g->builtin_types.entry_invalid;
}
return get_generic_fn_type(g, &fn_type_id);
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdEnumTag:
break;
}
return get_fn_type(g, &fn_type_id);
}
bool type_is_invalid(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
return true;
case TypeTableEntryIdStruct:
return type_entry->data.structure.is_invalid;
case TypeTableEntryIdEnum:
return type_entry->data.enumeration.is_invalid;
case TypeTableEntryIdUnion:
return type_entry->data.unionation.is_invalid;
case TypeTableEntryIdTypeDecl:
return type_is_invalid(type_entry->data.type_decl.canonical_type);
default:
return false;
}
zig_unreachable();
}
static TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnumTag);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "@enumTagType(%s)", buf_ptr(&enum_type->name));
entry->data.enum_tag.enum_type = enum_type;
entry->data.enum_tag.int_type = int_type;
entry->type_ref = int_type->type_ref;
entry->di_type = int_type->di_type;
entry->zero_bits = int_type->zero_bits;
return entry;
}
static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
// if you change this logic you likely must also change similar logic in parseh.cpp
assert(enum_type->id == TypeTableEntryIdEnum);
if (enum_type->data.enumeration.complete)
return;
resolve_enum_zero_bits(g, enum_type);
if (enum_type->data.enumeration.is_invalid)
return;
AstNode *decl_node = enum_type->data.enumeration.decl_node;
if (enum_type->data.enumeration.embedded_in_current) {
if (!enum_type->data.enumeration.reported_infinite_err) {
enum_type->data.enumeration.reported_infinite_err = true;
add_node_error(g, decl_node, buf_sprintf("enum '%s' contains itself", buf_ptr(&enum_type->name)));
}
return;
}
assert(!enum_type->data.enumeration.zero_bits_loop_flag);
assert(decl_node->type == NodeTypeContainerDecl);
assert(enum_type->di_type);
uint32_t field_count = enum_type->data.enumeration.src_field_count;
assert(enum_type->data.enumeration.fields);
ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
uint32_t gen_field_count = enum_type->data.enumeration.gen_field_count;
ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(gen_field_count);
TypeTableEntry *biggest_union_member = nullptr;
uint64_t biggest_align_in_bits = 0;
uint64_t biggest_union_member_size_in_bits = 0;
Scope *scope = &enum_type->data.enumeration.decls_scope->base;
ImportTableEntry *import = get_scope_import(scope);
// set temporary flag
enum_type->data.enumeration.embedded_in_current = true;
for (uint32_t i = 0; i < field_count; i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
TypeTableEntry *field_type = type_enum_field->type_entry;
di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i);
ensure_complete_type(g, field_type);
if (field_type->id == TypeTableEntryIdInvalid) {
enum_type->data.enumeration.is_invalid = true;
continue;
}
if (!type_has_bits(field_type))
continue;
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref);
assert(debug_size_in_bits > 0);
assert(debug_align_in_bits > 0);
union_inner_di_types[type_enum_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(enum_type->di_type), buf_ptr(type_enum_field->name),
import->di_file, field_node->line + 1,
debug_size_in_bits,
debug_align_in_bits,
0,
0, field_type->di_type);
biggest_align_in_bits = max(biggest_align_in_bits, debug_align_in_bits);
if (!biggest_union_member ||
debug_size_in_bits > biggest_union_member_size_in_bits)
{
biggest_union_member = field_type;
biggest_union_member_size_in_bits = debug_size_in_bits;
}
}
// unset temporary flag
enum_type->data.enumeration.embedded_in_current = false;
enum_type->data.enumeration.complete = true;
if (!enum_type->data.enumeration.is_invalid) {
enum_type->data.enumeration.union_type = biggest_union_member;
TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count);
TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type);
enum_type->data.enumeration.tag_type = tag_type_entry;
if (biggest_union_member) {
// create llvm type for union
LLVMTypeRef union_element_type = biggest_union_member->type_ref;
LLVMTypeRef union_type_ref = LLVMStructType(&union_element_type, 1, false);
// create llvm type for root struct
LLVMTypeRef root_struct_element_types[] = {
tag_type_entry->type_ref,
union_type_ref,
};
LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false);
// create debug type for tag
uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref);
ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum", import->di_file, decl_node->line + 1,
tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count,
tag_type_entry->di_type, "");
// create debug type for union
ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion", import->di_file, decl_node->line + 1,
biggest_union_member_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
gen_field_count, 0, "");
// create debug types for members of root struct
uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, 0);
ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(enum_type->di_type), "tag_field",
import->di_file, decl_node->line + 1,
tag_debug_size_in_bits,
tag_debug_align_in_bits,
tag_offset_in_bits,
0, tag_di_type);
uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, 1);
ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(enum_type->di_type), "union_field",
import->di_file, decl_node->line + 1,
biggest_union_member_size_in_bits,
biggest_align_in_bits,
union_offset_in_bits,
0, union_di_type);
// create debug type for root struct
ZigLLVMDIType *di_root_members[] = {
tag_member_di_type,
union_member_di_type,
};
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref);
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
ZigLLVMFileToScope(import->di_file),
buf_ptr(&enum_type->name),
import->di_file, decl_node->line + 1,
debug_size_in_bits,
debug_align_in_bits,
0, nullptr, di_root_members, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);
enum_type->di_type = replacement_di_type;
} else {
// create llvm type for root struct
enum_type->type_ref = tag_type_entry->type_ref;
// create debug type for tag
uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref);
ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name),
import->di_file, decl_node->line + 1,
tag_debug_size_in_bits,
tag_debug_align_in_bits,
di_enumerators, field_count,
tag_type_entry->di_type, "");
ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);
enum_type->di_type = tag_di_type;
}
}
}
static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
TypeTableEntry *canon_type = get_underlying_type(type_entry);
switch (canon_type->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
zig_unreachable();
case TypeTableEntryIdMetaType:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
return false;
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
return true;
case TypeTableEntryIdStruct:
return canon_type->data.structure.layout == ContainerLayoutPacked;
case TypeTableEntryIdMaybe:
{
TypeTableEntry *canon_child_type = get_underlying_type(canon_type->data.maybe.child_type);
return canon_child_type->id == TypeTableEntryIdPointer || canon_child_type->id == TypeTableEntryIdFn;
}
}
zig_unreachable();
}
static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
// if you change the logic of this function likely you must make a similar change in
// parseh.cpp
assert(struct_type->id == TypeTableEntryIdStruct);
if (struct_type->data.structure.complete)
return;
resolve_struct_zero_bits(g, struct_type);
if (struct_type->data.structure.is_invalid)
return;
AstNode *decl_node = struct_type->data.structure.decl_node;
if (struct_type->data.structure.embedded_in_current) {
struct_type->data.structure.is_invalid = true;
if (!struct_type->data.structure.reported_infinite_err) {
struct_type->data.structure.reported_infinite_err = true;
add_node_error(g, decl_node,
buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
}
return;
}
assert(!struct_type->data.structure.zero_bits_loop_flag);
assert(struct_type->data.structure.fields);
assert(decl_node->type == NodeTypeContainerDecl);
size_t field_count = struct_type->data.structure.src_field_count;
size_t gen_field_count = struct_type->data.structure.gen_field_count;
LLVMTypeRef *element_types = allocate<LLVMTypeRef>(gen_field_count);
// this field should be set to true only during the recursive calls to resolve_struct_type
struct_type->data.structure.embedded_in_current = true;
Scope *scope = &struct_type->data.structure.decls_scope->base;
size_t gen_field_index = 0;
bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
size_t packed_bits_offset = 0;
size_t first_packed_bits_offset_misalign = SIZE_MAX;
size_t debug_field_count = 0;
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
TypeTableEntry *field_type = type_struct_field->type_entry;
ensure_complete_type(g, field_type);
if (type_is_invalid(field_type)) {
struct_type->data.structure.is_invalid = true;
break;
}
if (!type_has_bits(field_type))
continue;
type_struct_field->gen_index = gen_field_index;
if (packed) {
if (!type_allowed_in_packed_struct(field_type)) {
AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
add_node_error(g, field_source_node,
buf_sprintf("packed structs cannot contain fields of type '%s'",
buf_ptr(&field_type->name)));
struct_type->data.structure.is_invalid = true;
break;
}
size_t field_size_in_bits = type_size_bits(g, field_type);
size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
type_struct_field->packed_bits_size = field_size_in_bits;
if (first_packed_bits_offset_misalign != SIZE_MAX) {
// this field is not byte-aligned; it is part of the previous field with a bit offset
type_struct_field->packed_bits_offset = packed_bits_offset - first_packed_bits_offset_misalign;
type_struct_field->unaligned_bit_count = field_size_in_bits;
size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign;
LLVMTypeRef int_type_ref = LLVMIntType(full_bit_count);
if (8 * LLVMStoreSizeOfType(g->target_data_ref, int_type_ref) == full_bit_count) {
// next field recovers store alignment
element_types[gen_field_index] = int_type_ref;
gen_field_index += 1;
first_packed_bits_offset_misalign = SIZE_MAX;
}
} else if (8 * LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref) != field_size_in_bits) {
first_packed_bits_offset_misalign = packed_bits_offset;
type_struct_field->packed_bits_offset = 0;
type_struct_field->unaligned_bit_count = field_size_in_bits;
} else {
// This is a byte-aligned field (both start and end) in a packed struct.
element_types[gen_field_index] = field_type->type_ref;
type_struct_field->packed_bits_offset = 0;
type_struct_field->unaligned_bit_count = 0;
gen_field_index += 1;
}
packed_bits_offset = next_packed_bits_offset;
} else {
element_types[gen_field_index] = field_type->type_ref;
assert(element_types[gen_field_index]);
gen_field_index += 1;
}
debug_field_count += 1;
}
if (first_packed_bits_offset_misalign != SIZE_MAX) {
size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign;
LLVMTypeRef int_type_ref = LLVMIntType(full_bit_count);
size_t store_bit_count = 8 * LLVMStoreSizeOfType(g->target_data_ref, int_type_ref);
element_types[gen_field_index] = LLVMIntType(store_bit_count);
gen_field_index += 1;
}
struct_type->data.structure.embedded_in_current = false;
struct_type->data.structure.complete = true;
if (struct_type->data.structure.is_invalid)
return;
if (struct_type->zero_bits) {
struct_type->type_ref = LLVMVoidType();
ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, g->builtin_types.entry_void->di_type);
struct_type->di_type = g->builtin_types.entry_void->di_type;
return;
}
assert(struct_type->di_type);
// the count may have been adjusting from packing bit fields
gen_field_count = gen_field_index;
struct_type->data.structure.gen_field_count = gen_field_count;
LLVMStructSetBody(struct_type->type_ref, element_types, gen_field_count, packed);
assert(LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref) > 0);
ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(debug_field_count);
ImportTableEntry *import = get_scope_import(scope);
size_t debug_field_index = 0;
for (size_t i = 0; i < field_count; i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
size_t gen_field_index = type_struct_field->gen_index;
if (gen_field_index == SIZE_MAX) {
continue;
}
TypeTableEntry *field_type = type_struct_field->type_entry;
// if the field is a function, actually the debug info should be a pointer.
ZigLLVMDIType *field_di_type;
if (field_type->id == TypeTableEntryIdFn) {
TypeTableEntry *field_ptr_type = get_pointer_to_type(g, field_type, true);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_ptr_type->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_ptr_type->type_ref);
field_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, field_type->di_type,
debug_size_in_bits, debug_align_in_bits, buf_ptr(&field_ptr_type->name));
} else {
field_di_type = field_type->di_type;
}
assert(field_type->type_ref);
assert(struct_type->type_ref);
assert(struct_type->data.structure.complete);
uint64_t debug_size_in_bits;
uint64_t debug_align_in_bits;
uint64_t debug_offset_in_bits;
if (packed) {
debug_size_in_bits = type_struct_field->packed_bits_size;
debug_align_in_bits = 1;
debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref,
gen_field_index) + type_struct_field->packed_bits_offset;
} else {
debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref);
debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref);
debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, gen_field_index);
}
di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name),
import->di_file, field_node->line + 1,
debug_size_in_bits,
debug_align_in_bits,
debug_offset_in_bits,
0, field_di_type);
assert(di_element_types[debug_field_index]);
debug_field_index += 1;
}
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, struct_type->type_ref);
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
ZigLLVMFileToScope(import->di_file),
buf_ptr(&struct_type->name),
import->di_file, decl_node->line + 1,
debug_size_in_bits,
debug_align_in_bits,
0, nullptr, di_element_types, debug_field_count, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
struct_type->di_type = replacement_di_type;
}
static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
zig_panic("TODO");
}
static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
assert(enum_type->id == TypeTableEntryIdEnum);
if (enum_type->data.enumeration.zero_bits_known)
return;
if (enum_type->data.enumeration.zero_bits_loop_flag) {
enum_type->data.enumeration.zero_bits_known = true;
return;
}
enum_type->data.enumeration.zero_bits_loop_flag = true;
AstNode *decl_node = enum_type->data.enumeration.decl_node;
assert(decl_node->type == NodeTypeContainerDecl);
assert(enum_type->di_type);
assert(!enum_type->data.enumeration.fields);
uint32_t field_count = decl_node->data.container_decl.fields.length;
enum_type->data.enumeration.src_field_count = field_count;
enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
Scope *scope = &enum_type->data.enumeration.decls_scope->base;
uint32_t gen_field_index = 0;
for (uint32_t i = 0; i < field_count; i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
type_enum_field->name = field_node->data.struct_field.name;
TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
type_enum_field->type_entry = field_type;
type_enum_field->value = i;
type_ensure_zero_bits_known(g, field_type);
if (field_type->id == TypeTableEntryIdInvalid) {
enum_type->data.enumeration.is_invalid = true;
continue;
}
if (!type_has_bits(field_type))
continue;
type_enum_field->gen_index = gen_field_index;
gen_field_index += 1;
}
enum_type->data.enumeration.zero_bits_loop_flag = false;
enum_type->data.enumeration.gen_field_count = gen_field_index;
enum_type->zero_bits = (gen_field_index == 0 && field_count < 2);
enum_type->data.enumeration.zero_bits_known = true;
}
static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
assert(struct_type->id == TypeTableEntryIdStruct);
if (struct_type->data.structure.zero_bits_known)
return;
if (struct_type->data.structure.zero_bits_loop_flag) {
struct_type->data.structure.zero_bits_known = true;
return;
}
struct_type->data.structure.zero_bits_loop_flag = true;
AstNode *decl_node = struct_type->data.structure.decl_node;
assert(decl_node->type == NodeTypeContainerDecl);
assert(struct_type->di_type);
assert(!struct_type->data.structure.fields);
size_t field_count = decl_node->data.container_decl.fields.length;
struct_type->data.structure.src_field_count = field_count;
struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
Scope *scope = &struct_type->data.structure.decls_scope->base;
size_t gen_field_index = 0;
for (size_t i = 0; i < field_count; i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
type_struct_field->name = field_node->data.struct_field.name;
TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
type_struct_field->type_entry = field_type;
type_struct_field->src_index = i;
type_struct_field->gen_index = SIZE_MAX;
type_ensure_zero_bits_known(g, field_type);
if (type_is_invalid(field_type)) {
struct_type->data.structure.is_invalid = true;
continue;
}
if (!type_has_bits(field_type))
continue;
type_struct_field->gen_index = gen_field_index;
gen_field_index += 1;
}
struct_type->data.structure.zero_bits_loop_flag = false;
struct_type->data.structure.gen_field_count = gen_field_index;
struct_type->zero_bits = (gen_field_index == 0);
struct_type->data.structure.zero_bits_known = true;
}
static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
zig_panic("TODO resolve_union_zero_bits");
}
static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {
if (!scope)
return;
if (scope->id == ScopeIdDecls) {
get_fully_qualified_decl_name_internal(buf, scope->parent, sep);
ScopeDecls *scope_decls = (ScopeDecls *)scope;
if (scope_decls->container_type) {
buf_append_buf(buf, &scope_decls->container_type->name);
buf_append_char(buf, sep);
}
return;
}
get_fully_qualified_decl_name_internal(buf, scope->parent, sep);
}
static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, uint8_t sep) {
buf_resize(buf, 0);
get_fully_qualified_decl_name_internal(buf, tld->parent_scope, sep);
buf_append_buf(buf, tld->name);
}
FnTableEntry *create_fn_raw(FnInline inline_value, bool internal_linkage) {
FnTableEntry *fn_entry = allocate<FnTableEntry>(1);
fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
fn_entry->analyzed_executable.backward_branch_quota = default_backward_branch_quota;
fn_entry->analyzed_executable.fn_entry = fn_entry;
fn_entry->ir_executable.fn_entry = fn_entry;
fn_entry->fn_inline = inline_value;
fn_entry->internal_linkage = internal_linkage;
return fn_entry;
}
FnTableEntry *create_fn(AstNode *proto_node) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
FnInline inline_value = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto;
bool internal_linkage = (fn_proto->visib_mod != VisibModExport && !proto_node->data.fn_proto.is_extern);
FnTableEntry *fn_entry = create_fn_raw(inline_value, internal_linkage);
fn_entry->proto_node = proto_node;
fn_entry->fn_def_node = proto_node->data.fn_proto.fn_def_node;
return fn_entry;
}
static bool scope_is_root_decls(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdDecls) {
ScopeDecls *scope_decls = (ScopeDecls *)scope;
return (scope_decls->container_type == nullptr);
}
scope = scope->parent;
}
zig_unreachable();
}
static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {
add_node_error(g, proto_node,
buf_sprintf("expected 'fn([]const u8) -> unreachable', found '%s'",
buf_ptr(&fn_type->name)));
}
static void typecheck_panic_fn(CodeGen *g) {
assert(g->panic_fn);
AstNode *proto_node = g->panic_fn->proto_node;
assert(proto_node->type == NodeTypeFnProto);
TypeTableEntry *fn_type = g->panic_fn->type_entry;
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
if (fn_type_id->param_count != 1) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
TypeTableEntry *const_u8_slice = get_slice_type(g, g->builtin_types.entry_u8, true);
if (fn_type_id->param_info[0].type != const_u8_slice) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
TypeTableEntry *actual_return_type = fn_type_id->return_type;
if (actual_return_type != g->builtin_types.entry_unreachable) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
}
static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
ImportTableEntry *import = tld_fn->base.import;
AstNode *proto_node = tld_fn->base.source_node;
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
AstNode *fn_def_node = fn_proto->fn_def_node;
FnTableEntry *fn_table_entry = create_fn(tld_fn->base.source_node);
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_');
tld_fn->fn_entry = fn_table_entry;
if (fn_table_entry->fn_def_node) {
fn_table_entry->fndef_scope = create_fndef_scope(
fn_table_entry->fn_def_node, tld_fn->base.parent_scope, fn_table_entry);
for (size_t i = 0; i < fn_proto->params.length; i += 1) {
AstNode *param_node = fn_proto->params.at(i);
assert(param_node->type == NodeTypeParamDecl);
if (buf_len(param_node->data.param_decl.name) == 0) {
add_node_error(g, param_node, buf_sprintf("missing parameter name"));
}
}
}
Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
fn_table_entry->type_entry = analyze_fn_type(g, proto_node, child_scope);
if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) {
tld_fn->base.resolution = TldResolutionInvalid;
return;
}
if (!fn_table_entry->type_entry->data.fn.is_generic) {
g->fn_protos.append(fn_table_entry);
if (fn_def_node)
g->fn_defs.append(fn_table_entry);
if (import == g->root_import && scope_is_root_decls(tld_fn->base.parent_scope)) {
if (buf_eql_str(&fn_table_entry->symbol_name, "main")) {
g->main_fn = fn_table_entry;
if (!g->link_libc && tld_fn->base.visib_mod != VisibModExport) {
TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
if (actual_return_type != err_void) {
add_node_error(g, fn_proto->return_type,
buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
buf_ptr(&actual_return_type->name)));
}
}
} else if (buf_eql_str(&fn_table_entry->symbol_name, "panic")) {
g->panic_fn = fn_table_entry;
typecheck_panic_fn(g);
}
} else if (import->package == g->panic_package && scope_is_root_decls(tld_fn->base.parent_scope)) {
if (buf_eql_str(&fn_table_entry->symbol_name, "panic")) {
g->panic_fn = fn_table_entry;
typecheck_panic_fn(g);
}
}
}
}
static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
if (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport ||
(buf_eql_str(tld->name, "panic") &&
(decls_scope->import->package == g->panic_package || decls_scope->import == g->root_import)))
{
g->resolve_queue.append(tld);
}
auto entry = decls_scope->decl_table.put_unique(tld->name, tld);
if (entry) {
Tld *other_tld = entry->value;
ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("redefinition of '%s'", buf_ptr(tld->name)));
add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition is here"));
return;
}
}
static void preview_error_value_decl(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeErrorValueDecl);
if (node->data.error_value_decl.visib_mod != VisibModPrivate) {
add_node_error(g, node, buf_sprintf("error values require no visibility modifier"));
}
ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
err->decl_node = node;
buf_init_from_buf(&err->name, node->data.error_value_decl.name);
auto existing_entry = g->error_table.maybe_get(&err->name);
if (existing_entry) {
// duplicate error definitions allowed and they get the same value
err->value = existing_entry->value->value;
} else {
size_t error_value_count = g->error_decls.length;
assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)g->err_tag_type->data.integral.bit_count));
err->value = error_value_count;
g->error_decls.append(node);
g->error_table.put(&err->name, err);
}
node->data.error_value_decl.err = err;
}
void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node,
Scope *parent_scope)
{
tld->id = id;
tld->name = name;
tld->visib_mod = visib_mod;
tld->source_node = source_node;
tld->import = source_node ? source_node->owner : nullptr;
tld->parent_scope = parent_scope;
}
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
switch (node->type) {
case NodeTypeRoot:
for (size_t i = 0; i < node->data.root.top_level_decls.length; i += 1) {
AstNode *child = node->data.root.top_level_decls.at(i);
scan_decls(g, decls_scope, child);
}
break;
case NodeTypeFnDef:
scan_decls(g, decls_scope, node->data.fn_def.fn_proto);
break;
case NodeTypeVariableDeclaration:
{
Buf *name = node->data.variable_declaration.symbol;
VisibMod visib_mod = node->data.variable_declaration.visib_mod;
TldVar *tld_var = allocate<TldVar>(1);
init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base);
add_top_level_decl(g, decls_scope, &tld_var->base);
break;
}
case NodeTypeTypeDecl:
{
Buf *name = node->data.type_decl.symbol;
VisibMod visib_mod = node->data.type_decl.visib_mod;
TldTypeDef *tld_typedef = allocate<TldTypeDef>(1);
init_tld(&tld_typedef->base, TldIdTypeDef, name, visib_mod, node, &decls_scope->base);
add_top_level_decl(g, decls_scope, &tld_typedef->base);
break;
}
case NodeTypeFnProto:
{
// if the name is missing, we immediately announce an error
Buf *fn_name = node->data.fn_proto.name;
if (buf_len(fn_name) == 0) {
add_node_error(g, node, buf_sprintf("missing function name"));
break;
}
VisibMod visib_mod = node->data.fn_proto.visib_mod;
TldFn *tld_fn = allocate<TldFn>(1);
init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base);
add_top_level_decl(g, decls_scope, &tld_fn->base);
break;
}
case NodeTypeUse:
{
g->use_queue.append(node);
ImportTableEntry *import = get_scope_import(&decls_scope->base);
import->use_decls.append(node);
break;
}
case NodeTypeErrorValueDecl:
// error value declarations do not depend on other top level decls
preview_error_value_decl(g, node);
break;
case NodeTypeContainerDecl:
case NodeTypeParamDecl:
case NodeTypeFnDecl:
case NodeTypeReturnExpr:
case NodeTypeDefer:
case NodeTypeBlock:
case NodeTypeBinOpExpr:
case NodeTypeUnwrapErrorExpr:
case NodeTypeFnCallExpr:
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeNumberLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeUndefinedLiteral:
case NodeTypeThisLiteral:
case NodeTypeSymbol:
case NodeTypePrefixOpExpr:
case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeCompTime:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeAsmExpr:
case NodeTypeFieldAccessExpr:
case NodeTypeStructField:
case NodeTypeContainerInitExpr:
case NodeTypeStructValueField:
case NodeTypeArrayType:
case NodeTypeErrorType:
case NodeTypeTypeLiteral:
case NodeTypeVarLiteral:
case NodeTypeTryExpr:
zig_unreachable();
}
}
static void resolve_decl_container(CodeGen *g, TldContainer *tld_container) {
TypeTableEntry *type_entry = tld_container->type_entry;
assert(type_entry);
switch (type_entry->id) {
case TypeTableEntryIdStruct:
resolve_struct_type(g, tld_container->type_entry);
return;
case TypeTableEntryIdEnum:
resolve_enum_type(g, tld_container->type_entry);
return;
case TypeTableEntryIdUnion:
resolve_union_type(g, tld_container->type_entry);
return;
default:
zig_unreachable();
}
}
TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
TypeTableEntry *underlying_type = get_underlying_type(type_entry);
switch (underlying_type->id) {
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdInvalid:
return g->builtin_types.entry_invalid;
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVar:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdBlock:
case TypeTableEntryIdArgTuple:
add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed",
buf_ptr(&underlying_type->name)));
return g->builtin_types.entry_invalid;
case TypeTableEntryIdNamespace:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
return type_entry;
}
zig_unreachable();
}
// Set name to nullptr to make the variable anonymous (not visible to programmer).
// TODO merge with definition of add_local_var in ir.cpp
VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
bool is_const, ConstExprValue *value)
{
assert(value);
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
variable_entry->value = value;
variable_entry->parent_scope = parent_scope;
variable_entry->shadowable = false;
variable_entry->mem_slot_index = SIZE_MAX;
variable_entry->src_arg_index = SIZE_MAX;
assert(name);
buf_init_from_buf(&variable_entry->name, name);
if (value->type->id != TypeTableEntryIdInvalid) {
VariableTableEntry *existing_var = find_variable(g, parent_scope, name);
if (existing_var && !existing_var->shadowable) {
ErrorMsg *msg = add_node_error(g, source_node,
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
variable_entry->value->type = g->builtin_types.entry_invalid;
} else {
auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
if (primitive_table_entry) {
TypeTableEntry *type = primitive_table_entry->value;
add_node_error(g, source_node,
buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
variable_entry->value->type = g->builtin_types.entry_invalid;
} else {
Tld *tld = find_decl(g, parent_scope, name);
if (tld && tld->id != TldIdVar) {
ErrorMsg *msg = add_node_error(g, source_node,
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here"));
variable_entry->value->type = g->builtin_types.entry_invalid;
}
}
}
}
Scope *child_scope;
if (source_node && source_node->type == NodeTypeParamDecl) {
child_scope = create_var_scope(source_node, parent_scope, variable_entry);
} else {
// it's already in the decls table
child_scope = parent_scope;
}
variable_entry->src_is_const = is_const;
variable_entry->gen_is_const = is_const;
variable_entry->decl_node = source_node;
variable_entry->child_scope = child_scope;
return variable_entry;
}
static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
AstNodeVariableDeclaration *var_decl = &tld_var->base.source_node->data.variable_declaration;
bool is_const = var_decl->is_const;
bool is_export = (tld_var->base.visib_mod == VisibModExport);
bool is_extern = var_decl->is_extern;
TypeTableEntry *explicit_type = nullptr;
if (var_decl->type) {
TypeTableEntry *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type);
explicit_type = validate_var_type(g, var_decl->type, proposed_type);
}
AstNode *source_node = tld_var->base.source_node;
if (is_export && is_extern) {
add_node_error(g, source_node, buf_sprintf("variable is both export and extern"));
}
VarLinkage linkage;
if (is_export) {
linkage = VarLinkageExport;
} else if (is_extern) {
linkage = VarLinkageExternal;
} else {
linkage = VarLinkageInternal;
}
IrInstruction *init_value = nullptr;
TypeTableEntry *implicit_type = nullptr;
if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) {
implicit_type = explicit_type;
} else if (var_decl->expr) {
init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol);
assert(init_value);
implicit_type = init_value->value.type;
if (implicit_type->id == TypeTableEntryIdUnreachable) {
add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable"));
implicit_type = g->builtin_types.entry_invalid;
} else if ((!is_const || linkage == VarLinkageExternal) &&
(implicit_type->id == TypeTableEntryIdNumLitFloat ||
implicit_type->id == TypeTableEntryIdNumLitInt))
{
add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == TypeTableEntryIdNullLit) {
add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
implicit_type = g->builtin_types.entry_invalid;
}
assert(implicit_type->id == TypeTableEntryIdInvalid || init_value->value.special != ConstValSpecialRuntime);
} else if (linkage != VarLinkageExternal) {
add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
implicit_type = g->builtin_types.entry_invalid;
}
TypeTableEntry *type = explicit_type ? explicit_type : implicit_type;
assert(type != nullptr); // should have been caught by the parser
ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type);
tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, is_const, init_val);
tld_var->var->linkage = linkage;
g->global_vars.append(tld_var);
}
static void resolve_decl_typedef(CodeGen *g, TldTypeDef *tld_typedef) {
AstNode *typedef_node = tld_typedef->base.source_node;
assert(typedef_node->type == NodeTypeTypeDecl);
AstNode *type_node = typedef_node->data.type_decl.child_type;
Buf *decl_name = typedef_node->data.type_decl.symbol;
TypeTableEntry *child_type = analyze_type_expr(g, tld_typedef->base.parent_scope, type_node);
tld_typedef->type_entry = (child_type->id == TypeTableEntryIdInvalid) ?
child_type : get_typedecl_type(g, buf_ptr(decl_name), child_type);
}
void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only) {
if (tld->resolution != TldResolutionUnresolved)
return;
if (tld->dep_loop_flag) {
add_node_error(g, tld->source_node, buf_sprintf("'%s' depends on itself", buf_ptr(tld->name)));
tld->resolution = TldResolutionInvalid;
return;
} else {
tld->dep_loop_flag = true;
}
switch (tld->id) {
case TldIdVar:
{
TldVar *tld_var = (TldVar *)tld;
resolve_decl_var(g, tld_var);
break;
}
case TldIdFn:
{
TldFn *tld_fn = (TldFn *)tld;
resolve_decl_fn(g, tld_fn);
break;
}
case TldIdContainer:
{
TldContainer *tld_container = (TldContainer *)tld;
resolve_decl_container(g, tld_container);
break;
}
case TldIdTypeDef:
{
TldTypeDef *tld_typedef = (TldTypeDef *)tld;
resolve_decl_typedef(g, tld_typedef);
break;
}
}
tld->resolution = TldResolutionOk;
tld->dep_loop_flag = false;
}
bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) {
if (expected_type == actual_type)
return true;
// pointer const
if (expected_type->id == TypeTableEntryIdPointer &&
actual_type->id == TypeTableEntryIdPointer &&
(!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) &&
(!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) &&
actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset &&
actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count)
{
return types_match_const_cast_only(expected_type->data.pointer.child_type,
actual_type->data.pointer.child_type);
}
// unknown size array const
if (expected_type->id == TypeTableEntryIdStruct &&
actual_type->id == TypeTableEntryIdStruct &&
expected_type->data.structure.is_slice &&
actual_type->data.structure.is_slice &&
(!actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
expected_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const) &&
(!actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_volatile ||
expected_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_volatile))
{
return types_match_const_cast_only(
expected_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type);
}
// maybe
if (expected_type->id == TypeTableEntryIdMaybe &&
actual_type->id == TypeTableEntryIdMaybe)
{
return types_match_const_cast_only(
expected_type->data.maybe.child_type,
actual_type->data.maybe.child_type);
}
// error
if (expected_type->id == TypeTableEntryIdErrorUnion &&
actual_type->id == TypeTableEntryIdErrorUnion)
{
return types_match_const_cast_only(
expected_type->data.error.child_type,
actual_type->data.error.child_type);
}
// fn
if (expected_type->id == TypeTableEntryIdFn &&
actual_type->id == TypeTableEntryIdFn)
{
if (expected_type->data.fn.fn_type_id.is_extern != actual_type->data.fn.fn_type_id.is_extern) {
return false;
}
if (expected_type->data.fn.fn_type_id.is_naked != actual_type->data.fn.fn_type_id.is_naked) {
return false;
}
if (expected_type->data.fn.fn_type_id.is_cold != actual_type->data.fn.fn_type_id.is_cold) {
return false;
}
if (actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable &&
!types_match_const_cast_only(
expected_type->data.fn.fn_type_id.return_type,
actual_type->data.fn.fn_type_id.return_type))
{
return false;
}
if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
return false;
}
for (size_t i = 0; i < expected_type->data.fn.fn_type_id.param_count; i += 1) {
// note it's reversed for parameters
FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];
if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) {
return false;
}
if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
return false;
}
}
return true;
}
return false;
}
Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
// we must resolve all the use decls
ImportTableEntry *import = get_scope_import(scope);
for (size_t i = 0; i < import->use_decls.length; i += 1) {
AstNode *use_decl_node = import->use_decls.at(i);
if (use_decl_node->data.use.resolution == TldResolutionUnresolved) {
preview_use_decl(g, use_decl_node);
}
resolve_use_decl(g, use_decl_node);
}
while (scope) {
if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
auto entry = decls_scope->decl_table.maybe_get(name);
if (entry)
return entry->value;
}
scope = scope->parent;
}
return nullptr;
}
VariableTableEntry *find_variable(CodeGen *g, Scope *scope, Buf *name) {
while (scope) {
if (scope->id == ScopeIdVarDecl) {
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
if (buf_eql_buf(name, &var_scope->var->name))
return var_scope->var;
} else if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
auto entry = decls_scope->decl_table.maybe_get(name);
if (entry) {
Tld *tld = entry->value;
if (tld->id == TldIdVar) {
TldVar *tld_var = (TldVar *)tld;
if (tld_var->var)
return tld_var->var;
}
}
}
scope = scope->parent;
}
return nullptr;
}
FnTableEntry *scope_fn_entry(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdFnDef) {
ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
return fn_scope->fn_entry;
}
scope = scope->parent;
}
return nullptr;
}
FnTableEntry *scope_get_fn_if_root(Scope *scope) {
assert(scope);
scope = scope->parent;
while (scope) {
switch (scope->id) {
case ScopeIdBlock:
return nullptr;
case ScopeIdDecls:
case ScopeIdDefer:
case ScopeIdDeferExpr:
case ScopeIdVarDecl:
case ScopeIdCImport:
case ScopeIdLoop:
case ScopeIdCompTime:
scope = scope->parent;
continue;
case ScopeIdFnDef:
ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
return fn_scope->fn_entry;
}
zig_unreachable();
}
return nullptr;
}
TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) {
for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
if (buf_eql_buf(type_enum_field->name, name)) {
return type_enum_field;
}
}
return nullptr;
}
TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
assert(type_entry->id == TypeTableEntryIdStruct);
assert(type_entry->data.structure.complete);
for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
TypeStructField *field = &type_entry->data.structure.fields[i];
if (buf_eql_buf(field->name, name)) {
return field;
}
}
return nullptr;
}
static bool is_container(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
zig_unreachable();
case TypeTableEntryIdStruct:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
return true;
case TypeTableEntryIdPointer:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdArray:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
return false;
}
zig_unreachable();
}
bool is_container_ref(TypeTableEntry *type_entry) {
return (type_entry->id == TypeTableEntryIdPointer) ?
is_container(type_entry->data.pointer.child_type) : is_container(type_entry);
}
TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) {
assert(is_container_ref(type_entry));
return (type_entry->id == TypeTableEntryIdPointer) ?
type_entry->data.pointer.child_type : type_entry;
}
void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdStruct:
resolve_struct_type(g, type_entry);
break;
case TypeTableEntryIdEnum:
resolve_enum_type(g, type_entry);
break;
case TypeTableEntryIdUnion:
resolve_union_type(g, type_entry);
break;
case TypeTableEntryIdPointer:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdArray:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
zig_unreachable();
}
}
bool type_is_codegen_pointer(TypeTableEntry *type) {
if (type->id == TypeTableEntryIdPointer) return true;
if (type->id == TypeTableEntryIdFn) return true;
if (type->id == TypeTableEntryIdMaybe) {
if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return true;
if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return true;
}
return false;
}
AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) {
if (fn_entry->param_source_nodes)
return fn_entry->param_source_nodes[index];
else if (fn_entry->proto_node)
return fn_entry->proto_node->data.fn_proto.params.at(index);
else
return nullptr;
}
void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) {
TypeTableEntry *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
Buf *param_name;
bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
if (param_decl_node && !is_var_args) {
param_name = param_decl_node->data.param_decl.name;
} else {
param_name = buf_sprintf("arg%zu", i);
}
TypeTableEntry *param_type = param_info->type;
bool is_noalias = param_info->is_noalias;
if (is_noalias && !type_is_codegen_pointer(param_type)) {
add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
}
if (fn_type_id->is_extern && handle_is_ptr(param_type)) {
add_node_error(g, param_decl_node,
buf_sprintf("byvalue types not yet supported on extern function parameters"));
}
VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
param_name, true, create_const_runtime(param_type));
var->src_arg_index = i;
fn_table_entry->child_scope = var->child_scope;
var->shadowable = var->shadowable || is_var_args;
if (type_has_bits(param_type)) {
fn_table_entry->variable_list.append(var);
}
if (fn_type->data.fn.gen_param_info) {
var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
}
if (arg_vars) {
arg_vars[i] = var;
}
}
}
void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) {
TypeTableEntry *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
TypeTableEntry *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable,
&fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
fn_table_entry->implicit_return_type = block_return_type;
if (block_return_type->id == TypeTableEntryIdInvalid ||
fn_table_entry->analyzed_executable.invalid)
{
assert(g->errors.length > 0);
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
if (g->verbose) {
fprintf(stderr, "{ // (analyzed)\n");
ir_print(stderr, &fn_table_entry->analyzed_executable, 4);
fprintf(stderr, "}\n");
}
fn_table_entry->anal_state = FnAnalStateComplete;
}
static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
assert(fn_table_entry->anal_state != FnAnalStateProbing);
if (fn_table_entry->anal_state != FnAnalStateReady)
return;
fn_table_entry->anal_state = FnAnalStateProbing;
AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;
assert(fn_table_entry->fndef_scope);
if (!fn_table_entry->child_scope)
fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
define_local_param_variables(g, fn_table_entry, nullptr);
TypeTableEntry *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
if (fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type)) {
add_node_error(g, return_type_node,
buf_sprintf("byvalue types not yet supported on extern function return values"));
}
ir_gen_fn(g, fn_table_entry);
if (fn_table_entry->ir_executable.invalid) {
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
if (g->verbose) {
fprintf(stderr, "\n");
ast_render(stderr, fn_table_entry->fn_def_node, 4);
fprintf(stderr, "\n{ // (IR)\n");
ir_print(stderr, &fn_table_entry->ir_executable, 4);
fprintf(stderr, "}\n");
}
analyze_fn_ir(g, fn_table_entry, return_type_node);
}
static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) {
IrInstruction *use_target_value = src_use_node->data.use.value;
if (use_target_value->value.type->id == TypeTableEntryIdInvalid) {
dst_use_node->owner->any_imports_failed = true;
return;
}
dst_use_node->data.use.resolution = TldResolutionOk;
ConstExprValue *const_val = &use_target_value->value;
assert(const_val->special != ConstValSpecialRuntime);
ImportTableEntry *target_import = const_val->data.x_import;
assert(target_import);
if (target_import->any_imports_failed) {
dst_use_node->owner->any_imports_failed = true;
}
auto it = target_import->decls_scope->decl_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
Tld *target_tld = entry->value;
if (target_tld->import != target_import ||
target_tld->visib_mod == VisibModPrivate)
{
continue;
}
auto existing_entry = dst_use_node->owner->decls_scope->decl_table.put_unique(target_tld->name, target_tld);
if (existing_entry) {
Tld *existing_decl = existing_entry->value;
if (existing_decl != target_tld) {
ErrorMsg *msg = add_node_error(g, dst_use_node,
buf_sprintf("import of '%s' overrides existing definition",
buf_ptr(target_tld->name)));
add_error_note(g, msg, existing_decl->source_node, buf_sprintf("previous definition here"));
add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here"));
}
}
}
for (size_t i = 0; i < target_import->use_decls.length; i += 1) {
AstNode *use_decl_node = target_import->use_decls.at(i);
if (use_decl_node->data.use.visib_mod != VisibModPrivate)
add_symbols_from_import(g, use_decl_node, dst_use_node);
}
}
void resolve_use_decl(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeUse);
if (node->data.use.resolution != TldResolutionUnresolved)
return;
add_symbols_from_import(g, node, node);
}
void preview_use_decl(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeUse);
IrInstruction *result = analyze_const_value(g, &node->owner->decls_scope->base,
node->data.use.expr, g->builtin_types.entry_namespace, nullptr);
if (result->value.type->id == TypeTableEntryIdInvalid)
node->owner->any_imports_failed = true;
node->data.use.value = result;
}
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package,
Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code)
{
Buf *full_path = buf_alloc();
os_path_join(src_dirname, src_basename, full_path);
if (g->verbose) {
fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(full_path));
fprintf(stderr, "----------------\n");
fprintf(stderr, "%s\n", buf_ptr(source_code));
fprintf(stderr, "\nTokens:\n");
fprintf(stderr, "---------\n");
}
Tokenization tokenization = {0};
tokenize(source_code, &tokenization);
if (tokenization.err) {
ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column,
source_code, tokenization.line_offsets, tokenization.err);
print_err_msg(err, g->err_color);
exit(1);
}
if (g->verbose) {
print_tokens(source_code, tokenization.tokens);
fprintf(stderr, "\nAST:\n");
fprintf(stderr, "------\n");
}
ImportTableEntry *import_entry = allocate<ImportTableEntry>(1);
import_entry->package = package;
import_entry->source_code = source_code;
import_entry->line_offsets = tokenization.line_offsets;
import_entry->path = full_path;
import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color,
&g->next_node_index);
assert(import_entry->root);
if (g->verbose) {
ast_print(stderr, import_entry->root, 0);
}
import_entry->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
g->import_table.put(abs_full_path, import_entry);
g->import_queue.append(import_entry);
import_entry->decls_scope = create_decls_scope(import_entry->root, nullptr, nullptr, import_entry);
assert(import_entry->root->type == NodeTypeRoot);
for (size_t decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) {
AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i);
if (top_level_decl->type == NodeTypeFnDef) {
AstNode *proto_node = top_level_decl->data.fn_def.fn_proto;
assert(proto_node->type == NodeTypeFnProto);
Buf *proto_name = proto_node->data.fn_proto.name;
bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub);
if (is_pub) {
if (buf_eql_str(proto_name, "main")) {
g->have_exported_main = true;
} else if (buf_eql_str(proto_name, "panic")) {
g->have_exported_panic = true;
}
}
}
}
return import_entry;
}
void semantic_analyze(CodeGen *g) {
for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) {
ImportTableEntry *import = g->import_queue.at(g->import_queue_index);
scan_decls(g, import->decls_scope, import->root);
}
for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) {
AstNode *use_decl_node = g->use_queue.at(g->use_queue_index);
preview_use_decl(g, use_decl_node);
}
for (size_t i = 0; i < g->use_queue.length; i += 1) {
AstNode *use_decl_node = g->use_queue.at(i);
resolve_use_decl(g, use_decl_node);
}
while (g->resolve_queue_index < g->resolve_queue.length ||
g->fn_defs_index < g->fn_defs.length)
{
for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
bool pointer_only = false;
resolve_top_level_decl(g, tld, pointer_only);
}
for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
FnTableEntry *fn_entry = g->fn_defs.at(g->fn_defs_index);
analyze_fn_body(g, fn_entry);
}
}
}
bool is_node_void_expr(AstNode *node) {
if (node->type == NodeTypeContainerInitExpr &&
node->data.container_init_expr.kind == ContainerInitKindArray)
{
AstNode *type_node = node->data.container_init_expr.type;
if (type_node->type == NodeTypeSymbol &&
buf_eql_str(type_node->data.symbol_expr.symbol, "void"))
{
return true;
}
} else if (node->type == NodeTypeBlock && node->data.block.statements.length == 0) {
return true;
}
return false;
}
TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint8_t size_in_bits) {
size_t index;
if (size_in_bits == 8) {
index = 0;
} else if (size_in_bits == 16) {
index = 1;
} else if (size_in_bits == 32) {
index = 2;
} else if (size_in_bits == 64) {
index = 3;
} else {
return nullptr;
}
return &g->builtin_types.entry_int[is_signed ? 0 : 1][index];
}
TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint8_t size_in_bits) {
TypeTableEntry **common_entry = get_int_type_ptr(g, is_signed, size_in_bits);
if (common_entry)
return *common_entry;
TypeId type_id = {};
type_id.id = TypeTableEntryIdInt;
type_id.data.integer.is_signed = is_signed;
type_id.data.integer.bit_count = size_in_bits;
{
auto entry = g->type_table.maybe_get(type_id);
if (entry)
return entry->value;
}
TypeTableEntry *new_entry = make_int_type(g, is_signed, size_in_bits);
g->type_table.put(type_id, new_entry);
return new_entry;
}
TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
return &g->builtin_types.entry_c_int[c_int_type];
}
TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type) {
return *get_c_int_type_ptr(g, c_int_type);
}
bool handle_is_ptr(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdVar:
case TypeTableEntryIdArgTuple:
zig_unreachable();
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdPureError:
case TypeTableEntryIdFn:
case TypeTableEntryIdEnumTag:
return false;
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdUnion:
return true;
case TypeTableEntryIdErrorUnion:
return type_has_bits(type_entry->data.error.child_type);
case TypeTableEntryIdEnum:
assert(type_entry->data.enumeration.complete);
return type_entry->data.enumeration.gen_field_count != 0;
case TypeTableEntryIdMaybe:
return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdFn;
case TypeTableEntryIdTypeDecl:
return handle_is_ptr(type_entry->data.type_decl.canonical_type);
}
zig_unreachable();
}
void find_libc_include_path(CodeGen *g) {
if (!g->libc_include_dir || buf_len(g->libc_include_dir) == 0) {
zig_panic("Unable to determine libc include path.");
}
}
void find_libc_lib_path(CodeGen *g) {
// later we can handle this better by reporting an error via the normal mechanism
if (!g->libc_lib_dir || buf_len(g->libc_lib_dir) == 0) {
zig_panic("Unable to determine libc lib path.");
}
if (!g->libc_static_lib_dir || buf_len(g->libc_static_lib_dir) == 0) {
zig_panic("Unable to determine libc static lib path.");
}
}
static uint32_t hash_ptr(void *ptr) {
return ((uintptr_t)ptr) % UINT32_MAX;
}
static uint32_t hash_size(size_t x) {
return x % UINT32_MAX;
}
uint32_t fn_table_entry_hash(FnTableEntry* value) {
return ptr_hash(value);
}
bool fn_table_entry_eql(FnTableEntry *a, FnTableEntry *b) {
return ptr_eq(a, b);
}
uint32_t fn_type_id_hash(FnTypeId *id) {
uint32_t result = 0;
result += id->is_extern ? 3349388391 : 0;
result += id->is_naked ? 608688877 : 0;
result += id->is_cold ? 3605523458 : 0;
result += id->is_var_args ? 1931444534 : 0;
result += hash_ptr(id->return_type);
for (size_t i = 0; i < id->param_count; i += 1) {
FnTypeParamInfo *info = &id->param_info[i];
result += info->is_noalias ? 892356923 : 0;
result += hash_ptr(info->type);
}
return result;
}
bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
if (a->is_extern != b->is_extern ||
a->is_naked != b->is_naked ||
a->is_cold != b->is_cold ||
a->return_type != b->return_type ||
a->is_var_args != b->is_var_args ||
a->param_count != b->param_count)
{
return false;
}
for (size_t i = 0; i < a->param_count; i += 1) {
FnTypeParamInfo *a_param_info = &a->param_info[i];
FnTypeParamInfo *b_param_info = &b->param_info[i];
if (a_param_info->type != b_param_info->type ||
a_param_info->is_noalias != b_param_info->is_noalias)
{
return false;
}
}
return true;
}
static uint32_t hash_const_val(ConstExprValue *const_val) {
assert(const_val->special == ConstValSpecialStatic);
switch (const_val->type->id) {
case TypeTableEntryIdBool:
return const_val->data.x_bool ? 127863866 : 215080464;
case TypeTableEntryIdMetaType:
return hash_ptr(const_val->data.x_type);
case TypeTableEntryIdVoid:
return 4149439618;
case TypeTableEntryIdInt:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdEnumTag:
return ((uint32_t)(bignum_to_twos_complement(&const_val->data.x_bignum) % UINT32_MAX)) * 1331471175;
case TypeTableEntryIdFloat:
case TypeTableEntryIdNumLitFloat:
return const_val->data.x_bignum.data.x_float * UINT32_MAX;
case TypeTableEntryIdArgTuple:
return const_val->data.x_arg_tuple.start_index * 281907309 +
const_val->data.x_arg_tuple.end_index * 2290442768;
case TypeTableEntryIdPointer:
{
uint32_t hash_val = 0;
switch (const_val->data.x_ptr.mut) {
case ConstPtrMutRuntimeVar:
hash_val += 3500721036;
break;
case ConstPtrMutComptimeConst:
hash_val += 4214318515;
break;
case ConstPtrMutComptimeVar:
hash_val += 1103195694;
break;
}
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
hash_val += 2478261866;
hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
return hash_val;
case ConstPtrSpecialBaseArray:
hash_val += 1764906839;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
return hash_val;
case ConstPtrSpecialBaseStruct:
hash_val += 3518317043;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
return hash_val;
case ConstPtrSpecialHardCodedAddr:
hash_val += 4048518294;
hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
return hash_val;
case ConstPtrSpecialDiscard:
hash_val += 2010123162;
return hash_val;
}
zig_unreachable();
}
case TypeTableEntryIdUndefLit:
return 162837799;
case TypeTableEntryIdNullLit:
return 844854567;
case TypeTableEntryIdArray:
// TODO better hashing algorithm
return 1166190605;
case TypeTableEntryIdStruct:
// TODO better hashing algorithm
return 1532530855;
case TypeTableEntryIdUnion:
// TODO better hashing algorithm
return 2709806591;
case TypeTableEntryIdMaybe:
if (const_val->data.x_maybe) {
return hash_const_val(const_val->data.x_maybe) * 1992916303;
} else {
return 4016830364;
}
case TypeTableEntryIdErrorUnion:
// TODO better hashing algorithm
return 3415065496;
case TypeTableEntryIdPureError:
// TODO better hashing algorithm
return 2630160122;
case TypeTableEntryIdEnum:
// TODO better hashing algorithm
return 31643936;
case TypeTableEntryIdFn:
return hash_ptr(const_val->data.x_fn);
case TypeTableEntryIdTypeDecl:
return hash_ptr(const_val->data.x_type);
case TypeTableEntryIdNamespace:
return hash_ptr(const_val->data.x_import);
case TypeTableEntryIdBlock:
return hash_ptr(const_val->data.x_block);
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdInvalid:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVar:
zig_unreachable();
}
zig_unreachable();
}
uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) {
uint32_t result = 0;
result += hash_ptr(id->fn_entry);
for (size_t i = 0; i < id->param_count; i += 1) {
ConstExprValue *generic_param = &id->params[i];
if (generic_param->special != ConstValSpecialRuntime) {
result += hash_const_val(generic_param);
result += hash_ptr(generic_param->type);
}
}
return result;
}
bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
assert(a->fn_entry);
if (a->fn_entry != b->fn_entry) return false;
if (a->param_count != b->param_count) return false;
for (size_t i = 0; i < a->param_count; i += 1) {
ConstExprValue *a_val = &a->params[i];
ConstExprValue *b_val = &b->params[i];
if (a_val->type != b_val->type) return false;
if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) {
assert(a_val->special == ConstValSpecialStatic);
assert(b_val->special == ConstValSpecialStatic);
if (!const_values_equal(a_val, b_val)) {
return false;
}
} else {
assert(a_val->special == ConstValSpecialRuntime && b_val->special == ConstValSpecialRuntime);
}
}
return true;
}
uint32_t fn_eval_hash(Scope* scope) {
uint32_t result = 0;
while (scope) {
if (scope->id == ScopeIdVarDecl) {
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
result += hash_const_val(var_scope->var->value);
} else if (scope->id == ScopeIdFnDef) {
ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
result += hash_ptr(fn_scope->fn_entry);
return result;
} else {
zig_unreachable();
}
scope = scope->parent;
}
zig_unreachable();
}
bool fn_eval_eql(Scope *a, Scope *b) {
while (a && b) {
if (a->id != b->id)
return false;
if (a->id == ScopeIdVarDecl) {
ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a;
ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b;
if (a_var_scope->var->value->type != b_var_scope->var->value->type)
return false;
if (!const_values_equal(a_var_scope->var->value, b_var_scope->var->value))
return false;
} else if (a->id == ScopeIdFnDef) {
ScopeFnDef *a_fn_scope = (ScopeFnDef *)a;
ScopeFnDef *b_fn_scope = (ScopeFnDef *)b;
if (a_fn_scope->fn_entry != b_fn_scope->fn_entry)
return false;
return true;
} else {
zig_unreachable();
}
a = a->parent;
b = b->parent;
}
return false;
}
bool type_has_bits(TypeTableEntry *type_entry) {
assert(type_entry);
assert(type_entry->id != TypeTableEntryIdInvalid);
assert(type_has_zero_bits_known(type_entry));
return !type_entry->zero_bits;
}
bool type_requires_comptime(TypeTableEntry *type_entry) {
switch (get_underlying_type(type_entry)->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVar:
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
return true;
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdUnion:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdEnum:
case TypeTableEntryIdPureError:
case TypeTableEntryIdFn:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdVoid:
return false;
}
zig_unreachable();
}
void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
const_val->special = ConstValSpecialStatic;
const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str));
const_val->data.x_array.elements = allocate<ConstExprValue>(buf_len(str));
for (size_t i = 0; i < buf_len(str); i += 1) {
ConstExprValue *this_char = &const_val->data.x_array.elements[i];
this_char->special = ConstValSpecialStatic;
this_char->type = g->builtin_types.entry_u8;
bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]);
}
}
ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_str_lit(g, const_val, str);
return const_val;
}
void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
// first we build the underlying array
size_t len_with_null = buf_len(str) + 1;
ConstExprValue *array_val = allocate<ConstExprValue>(1);
array_val->special = ConstValSpecialStatic;
array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null);
array_val->data.x_array.elements = allocate<ConstExprValue>(len_with_null);
for (size_t i = 0; i < buf_len(str); i += 1) {
ConstExprValue *this_char = &array_val->data.x_array.elements[i];
this_char->special = ConstValSpecialStatic;
this_char->type = g->builtin_types.entry_u8;
bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]);
}
ConstExprValue *null_char = &array_val->data.x_array.elements[len_with_null - 1];
null_char->special = ConstValSpecialStatic;
null_char->type = g->builtin_types.entry_u8;
bignum_init_unsigned(&null_char->data.x_bignum, 0);
// then make the pointer point to it
const_val->special = ConstValSpecialStatic;
const_val->type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
const_val->data.x_ptr.data.base_array.array_val = array_val;
const_val->data.x_ptr.data.base_array.elem_index = 0;
const_val->data.x_ptr.data.base_array.is_cstr = true;
}
ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_c_str_lit(g, const_val, str);
return const_val;
}
void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
bignum_init_unsigned(&const_val->data.x_bignum, x);
const_val->data.x_bignum.is_negative = negative;
}
ConstExprValue *create_const_unsigned_negative(TypeTableEntry *type, uint64_t x, bool negative) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_unsigned_negative(const_val, type, x, negative);
return const_val;
}
void init_const_usize(CodeGen *g, ConstExprValue *const_val, uint64_t x) {
return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false);
}
ConstExprValue *create_const_usize(CodeGen *g, uint64_t x) {
return create_const_unsigned_negative(g->builtin_types.entry_usize, x, false);
}
void init_const_signed(ConstExprValue *const_val, TypeTableEntry *type, int64_t x) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
bignum_init_signed(&const_val->data.x_bignum, x);
}
ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_signed(const_val, type, x);
return const_val;
}
void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
bignum_init_float(&const_val->data.x_bignum, value);
}
ConstExprValue *create_const_float(TypeTableEntry *type, double value) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_float(const_val, type, value);
return const_val;
}
void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, uint64_t tag) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
const_val->data.x_enum.tag = tag;
}
ConstExprValue *create_const_enum_tag(TypeTableEntry *type, uint64_t tag) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_enum_tag(const_val, type, tag);
return const_val;
}
void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value) {
const_val->special = ConstValSpecialStatic;
const_val->type = g->builtin_types.entry_bool;
const_val->data.x_bool = value;
}
ConstExprValue *create_const_bool(CodeGen *g, bool value) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_bool(g, const_val, value);
return const_val;
}
void init_const_runtime(ConstExprValue *const_val, TypeTableEntry *type) {
const_val->special = ConstValSpecialRuntime;
const_val->type = type;
}
ConstExprValue *create_const_runtime(TypeTableEntry *type) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_runtime(const_val, type);
return const_val;
}
void init_const_type(CodeGen *g, ConstExprValue *const_val, TypeTableEntry *type_value) {
const_val->special = ConstValSpecialStatic;
const_val->type = g->builtin_types.entry_type;
const_val->data.x_type = type_value;
}
ConstExprValue *create_const_type(CodeGen *g, TypeTableEntry *type_value) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_type(g, const_val, type_value);
return const_val;
}
void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
size_t start, size_t len, bool is_const)
{
assert(array_val->type->id == TypeTableEntryIdArray);
const_val->special = ConstValSpecialStatic;
const_val->type = get_slice_type(g, array_val->type->data.array.child_type, is_const);
const_val->data.x_struct.fields = allocate<ConstExprValue>(2);
init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const);
init_const_usize(g, &const_val->data.x_struct.fields[slice_len_index], len);
}
ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t start, size_t len, bool is_const) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_slice(g, const_val, array_val, start, len, is_const);
return const_val;
}
void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
size_t elem_index, bool is_const)
{
assert(array_val->type->id == TypeTableEntryIdArray);
TypeTableEntry *child_type = array_val->type->data.array.child_type;
const_val->special = ConstValSpecialStatic;
const_val->type = get_pointer_to_type(g, child_type, is_const);
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
const_val->data.x_ptr.data.base_array.array_val = array_val;
const_val->data.x_ptr.data.base_array.elem_index = elem_index;
}
ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_ptr_array(g, const_val, array_val, elem_index, is_const);
return const_val;
}
void init_const_ptr_ref(CodeGen *g, ConstExprValue *const_val, ConstExprValue *pointee_val, bool is_const) {
const_val->special = ConstValSpecialStatic;
const_val->type = get_pointer_to_type(g, pointee_val->type, is_const);
const_val->data.x_ptr.special = ConstPtrSpecialRef;
const_val->data.x_ptr.data.ref.pointee = pointee_val;
}
ConstExprValue *create_const_ptr_ref(CodeGen *g, ConstExprValue *pointee_val, bool is_const) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_ptr_ref(g, const_val, pointee_val, is_const);
return const_val;
}
void init_const_arg_tuple(CodeGen *g, ConstExprValue *const_val, size_t arg_index_start, size_t arg_index_end) {
const_val->special = ConstValSpecialStatic;
const_val->type = g->builtin_types.entry_arg_tuple;
const_val->data.x_arg_tuple.start_index = arg_index_start;
const_val->data.x_arg_tuple.end_index = arg_index_end;
}
ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_t arg_index_end) {
ConstExprValue *const_val = allocate<ConstExprValue>(1);
init_const_arg_tuple(g, const_val, arg_index_start, arg_index_end);
return const_val;
}
void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
TypeTableEntry *canon_wanted_type = get_underlying_type(const_val->type);
if (canon_wanted_type->id == TypeTableEntryIdArray) {
const_val->special = ConstValSpecialStatic;
size_t elem_count = canon_wanted_type->data.array.len;
const_val->data.x_array.elements = allocate<ConstExprValue>(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *element_val = &const_val->data.x_array.elements[i];
element_val->type = canon_wanted_type->data.array.child_type;
init_const_undefined(g, element_val);
ConstParent *parent = get_const_val_parent(element_val);
if (parent != nullptr) {
parent->id = ConstParentIdArray;
parent->data.p_array.array_val = const_val;
parent->data.p_array.elem_index = i;
}
}
} else if (canon_wanted_type->id == TypeTableEntryIdStruct) {
ensure_complete_type(g, canon_wanted_type);
const_val->special = ConstValSpecialStatic;
size_t field_count = canon_wanted_type->data.structure.src_field_count;
const_val->data.x_struct.fields = allocate<ConstExprValue>(field_count);
for (size_t i = 0; i < field_count; i += 1) {
ConstExprValue *field_val = &const_val->data.x_struct.fields[i];
field_val->type = canon_wanted_type->data.structure.fields[i].type_entry;
assert(field_val->type);
init_const_undefined(g, field_val);
ConstParent *parent = get_const_val_parent(field_val);
if (parent != nullptr) {
parent->id = ConstParentIdStruct;
parent->data.p_struct.struct_val = const_val;
parent->data.p_struct.field_index = i;
}
}
} else {
const_val->special = ConstValSpecialUndef;
}
}
void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry) {
if (type_entry->id == TypeTableEntryIdStruct) {
if (!type_entry->data.structure.complete)
resolve_struct_type(g, type_entry);
} else if (type_entry->id == TypeTableEntryIdEnum) {
if (!type_entry->data.enumeration.complete)
resolve_enum_type(g, type_entry);
} else if (type_entry->id == TypeTableEntryIdUnion) {
if (!type_entry->data.unionation.complete)
resolve_union_type(g, type_entry);
}
}
void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry) {
if (type_entry->id == TypeTableEntryIdStruct) {
resolve_struct_zero_bits(g, type_entry);
} else if (type_entry->id == TypeTableEntryIdEnum) {
resolve_enum_zero_bits(g, type_entry);
} else if (type_entry->id == TypeTableEntryIdUnion) {
resolve_union_zero_bits(g, type_entry);
}
}
bool ir_get_var_is_comptime(VariableTableEntry *var) {
if (!var->is_comptime)
return false;
if (var->is_comptime->other)
return var->is_comptime->other->value.data.x_bool;
return var->is_comptime->value.data.x_bool;
}
bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
assert(a->type->id == b->type->id);
assert(a->special == ConstValSpecialStatic);
assert(b->special == ConstValSpecialStatic);
switch (a->type->id) {
case TypeTableEntryIdEnum:
{
ConstEnumValue *enum1 = &a->data.x_enum;
ConstEnumValue *enum2 = &b->data.x_enum;
if (enum1->tag == enum2->tag) {
TypeEnumField *enum_field = &a->type->data.enumeration.fields[enum1->tag];
if (type_has_bits(enum_field->type_entry)) {
zig_panic("TODO const expr analyze enum special value for equality");
} else {
return true;
}
}
return false;
}
case TypeTableEntryIdMetaType:
return a->data.x_type == b->data.x_type;
case TypeTableEntryIdVoid:
return true;
case TypeTableEntryIdPureError:
return a->data.x_pure_err == b->data.x_pure_err;
case TypeTableEntryIdFn:
return a->data.x_fn == b->data.x_fn;
case TypeTableEntryIdBool:
return a->data.x_bool == b->data.x_bool;
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdEnumTag:
return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum);
case TypeTableEntryIdPointer:
if (a->data.x_ptr.special != b->data.x_ptr.special)
return false;
if (a->data.x_ptr.mut != b->data.x_ptr.mut)
return false;
switch (a->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
return false;
return true;
case ConstPtrSpecialBaseArray:
if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val)
return false;
if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
return false;
if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
return false;
return true;
case ConstPtrSpecialBaseStruct:
if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val)
return false;
if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
return false;
return true;
case ConstPtrSpecialHardCodedAddr:
if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
return false;
return true;
case ConstPtrSpecialDiscard:
return true;
}
zig_unreachable();
case TypeTableEntryIdArray:
zig_panic("TODO");
case TypeTableEntryIdStruct:
for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
ConstExprValue *field_a = &a->data.x_struct.fields[i];
ConstExprValue *field_b = &b->data.x_struct.fields[i];
if (!const_values_equal(field_a, field_b))
return false;
}
return true;
case TypeTableEntryIdUnion:
zig_panic("TODO");
case TypeTableEntryIdUndefLit:
zig_panic("TODO");
case TypeTableEntryIdNullLit:
zig_panic("TODO");
case TypeTableEntryIdMaybe:
zig_panic("TODO");
case TypeTableEntryIdErrorUnion:
zig_panic("TODO");
case TypeTableEntryIdTypeDecl:
zig_panic("TODO");
case TypeTableEntryIdNamespace:
return a->data.x_import == b->data.x_import;
case TypeTableEntryIdBlock:
return a->data.x_block == b->data.x_block;
case TypeTableEntryIdArgTuple:
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdInvalid:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVar:
zig_unreachable();
}
zig_unreachable();
}
uint64_t max_unsigned_val(TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.bit_count == 64) {
return UINT64_MAX;
} else {
return (((uint64_t)1) << type_entry->data.integral.bit_count) - 1;
}
}
static int64_t max_signed_val(TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.bit_count == 64) {
return INT64_MAX;
} else {
return (((uint64_t)1) << (type_entry->data.integral.bit_count - 1)) - 1;
}
}
int64_t min_signed_val(TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.bit_count == 64) {
return INT64_MIN;
} else {
return -((int64_t)(((uint64_t)1) << (type_entry->data.integral.bit_count - 1)));
}
}
void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max) {
if (type_entry->id == TypeTableEntryIdInt) {
const_val->special = ConstValSpecialStatic;
if (is_max) {
if (type_entry->data.integral.is_signed) {
int64_t val = max_signed_val(type_entry);
bignum_init_signed(&const_val->data.x_bignum, val);
} else {
uint64_t val = max_unsigned_val(type_entry);
bignum_init_unsigned(&const_val->data.x_bignum, val);
}
} else {
if (type_entry->data.integral.is_signed) {
int64_t val = min_signed_val(type_entry);
bignum_init_signed(&const_val->data.x_bignum, val);
} else {
bignum_init_unsigned(&const_val->data.x_bignum, 0);
}
}
} else if (type_entry->id == TypeTableEntryIdFloat) {
zig_panic("TODO analyze_min_max_value float");
} else if (type_entry->id == TypeTableEntryIdBool) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_bool = is_max;
} else if (type_entry->id == TypeTableEntryIdVoid) {
// nothing to do
} else {
zig_unreachable();
}
}
void render_const_value(Buf *buf, ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
buf_appendf(buf, "(runtime value)");
return;
case ConstValSpecialUndef:
buf_appendf(buf, "undefined");
return;
case ConstValSpecialStatic:
break;
}
assert(const_val->type);
TypeTableEntry *canon_type = get_underlying_type(const_val->type);
switch (canon_type->id) {
case TypeTableEntryIdTypeDecl:
zig_unreachable();
case TypeTableEntryIdInvalid:
buf_appendf(buf, "(invalid)");
return;
case TypeTableEntryIdVar:
buf_appendf(buf, "(var)");
return;
case TypeTableEntryIdVoid:
buf_appendf(buf, "{}");
return;
case TypeTableEntryIdNumLitFloat:
buf_appendf(buf, "%f", const_val->data.x_bignum.data.x_float);
return;
case TypeTableEntryIdNumLitInt:
{
BigNum *bignum = &const_val->data.x_bignum;
const char *negative_str = bignum->is_negative ? "-" : "";
buf_appendf(buf, "%s%llu", negative_str, bignum->data.x_uint);
return;
}
case TypeTableEntryIdMetaType:
buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name));
return;
case TypeTableEntryIdInt:
{
BigNum *bignum = &const_val->data.x_bignum;
assert(bignum->kind == BigNumKindInt);
const char *negative_str = bignum->is_negative ? "-" : "";
buf_appendf(buf, "%s%llu", negative_str, bignum->data.x_uint);
}
return;
case TypeTableEntryIdFloat:
{
BigNum *bignum = &const_val->data.x_bignum;
assert(bignum->kind == BigNumKindFloat);
buf_appendf(buf, "%f", bignum->data.x_float);
}
return;
case TypeTableEntryIdUnreachable:
buf_appendf(buf, "@unreachable()");
return;
case TypeTableEntryIdBool:
{
const char *value = const_val->data.x_bool ? "true" : "false";
buf_appendf(buf, "%s", value);
return;
}
case TypeTableEntryIdPointer:
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
case ConstPtrSpecialBaseStruct:
buf_appendf(buf, "&");
render_const_value(buf, const_ptr_pointee(const_val));
return;
case ConstPtrSpecialBaseArray:
if (const_val->data.x_ptr.data.base_array.is_cstr) {
buf_appendf(buf, "&(c str lit)");
return;
} else {
buf_appendf(buf, "&");
render_const_value(buf, const_ptr_pointee(const_val));
return;
}
case ConstPtrSpecialHardCodedAddr:
buf_appendf(buf, "(&%s)(%" PRIx64 ")", buf_ptr(&canon_type->data.pointer.child_type->name),
const_val->data.x_ptr.data.hard_coded_addr.addr);
return;
case ConstPtrSpecialDiscard:
buf_append_str(buf, "&_");
return;
}
zig_unreachable();
case TypeTableEntryIdFn:
{
FnTableEntry *fn_entry = const_val->data.x_fn;
buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name));
return;
}
case TypeTableEntryIdBlock:
{
AstNode *node = const_val->data.x_block->source_node;
buf_appendf(buf, "(scope:%zu:%zu)", node->line + 1, node->column + 1);
return;
}
case TypeTableEntryIdArray:
{
TypeTableEntry *child_type = canon_type->data.array.child_type;
uint64_t len = canon_type->data.array.len;
// if it's []u8, assume UTF-8 and output a string
if (child_type->id == TypeTableEntryIdInt &&
child_type->data.integral.bit_count == 8 &&
!child_type->data.integral.is_signed)
{
buf_append_char(buf, '"');
for (uint64_t i = 0; i < len; i += 1) {
ConstExprValue *child_value = &const_val->data.x_array.elements[i];
uint64_t big_c = child_value->data.x_bignum.data.x_uint;
assert(big_c <= UINT8_MAX);
uint8_t c = big_c;
if (c == '"') {
buf_append_str(buf, "\\\"");
} else {
buf_append_char(buf, c);
}
}
buf_append_char(buf, '"');
return;
}
buf_appendf(buf, "%s{", buf_ptr(&canon_type->name));
for (uint64_t i = 0; i < len; i += 1) {
if (i != 0)
buf_appendf(buf, ",");
ConstExprValue *child_value = &const_val->data.x_array.elements[i];
render_const_value(buf, child_value);
}
buf_appendf(buf, "}");
return;
}
case TypeTableEntryIdNullLit:
{
buf_appendf(buf, "null");
return;
}
case TypeTableEntryIdUndefLit:
{
buf_appendf(buf, "undefined");
return;
}
case TypeTableEntryIdMaybe:
{
if (const_val->data.x_maybe) {
render_const_value(buf, const_val->data.x_maybe);
} else {
buf_appendf(buf, "null");
}
return;
}
case TypeTableEntryIdNamespace:
{
ImportTableEntry *import = const_val->data.x_import;
if (import->c_import_node) {
buf_appendf(buf, "(namespace from C import)");
} else {
buf_appendf(buf, "(namespace: %s)", buf_ptr(import->path));
}
return;
}
case TypeTableEntryIdBoundFn:
{
FnTableEntry *fn_entry = const_val->data.x_bound_fn.fn;
buf_appendf(buf, "(bound fn %s)", buf_ptr(&fn_entry->symbol_name));
return;
}
case TypeTableEntryIdStruct:
{
buf_appendf(buf, "(struct %s constant)", buf_ptr(&canon_type->name));
return;
}
case TypeTableEntryIdEnum:
{
buf_appendf(buf, "(enum %s constant)", buf_ptr(&canon_type->name));
return;
}
case TypeTableEntryIdErrorUnion:
{
buf_appendf(buf, "(error union %s constant)", buf_ptr(&canon_type->name));
return;
}
case TypeTableEntryIdUnion:
{
buf_appendf(buf, "(union %s constant)", buf_ptr(&canon_type->name));
return;
}
case TypeTableEntryIdPureError:
{
buf_appendf(buf, "(pure error constant)");
return;
}
case TypeTableEntryIdEnumTag:
{
TypeTableEntry *enum_type = canon_type->data.enum_tag.enum_type;
TypeEnumField *field = &enum_type->data.enumeration.fields[const_val->data.x_bignum.data.x_uint];
buf_appendf(buf, "%s.%s", buf_ptr(&enum_type->name), buf_ptr(field->name));
return;
}
case TypeTableEntryIdArgTuple:
{
buf_appendf(buf, "(args value)");
return;
}
}
zig_unreachable();
}
TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, size_t size_in_bits) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(size_in_bits);
const char u_or_i = is_signed ? 'i' : 'u';
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "%c%zu", u_or_i, size_in_bits);
unsigned dwarf_tag;
if (is_signed) {
if (size_in_bits == 8) {
dwarf_tag = ZigLLVMEncoding_DW_ATE_signed_char();
} else {
dwarf_tag = ZigLLVMEncoding_DW_ATE_signed();
}
} else {
if (size_in_bits == 8) {
dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned_char();
} else {
dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned();
}
}
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), debug_size_in_bits, dwarf_tag);
entry->data.integral.is_signed = is_signed;
entry->data.integral.bit_count = size_in_bits;
return entry;
}
uint32_t type_id_hash(TypeId x) {
switch (x.id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdFloat:
case TypeTableEntryIdStruct:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
zig_unreachable();
case TypeTableEntryIdPointer:
return hash_ptr(x.data.pointer.child_type) +
(x.data.pointer.is_const ? 2749109194 : 4047371087) +
(x.data.pointer.is_volatile ? 536730450 : 1685612214) +
(((uint32_t)x.data.pointer.bit_offset) * 2639019452) +
(((uint32_t)x.data.pointer.unaligned_bit_count) * 529908881);
case TypeTableEntryIdArray:
return hash_ptr(x.data.array.child_type) +
(x.data.array.size * 2122979968);
case TypeTableEntryIdInt:
return (x.data.integer.is_signed ? 2652528194 : 163929201) +
(((uint32_t)x.data.integer.bit_count) * 2998081557);
}
zig_unreachable();
}
bool type_id_eql(TypeId a, TypeId b) {
if (a.id != b.id)
return false;
switch (a.id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdFloat:
case TypeTableEntryIdStruct:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
zig_unreachable();
case TypeTableEntryIdPointer:
return a.data.pointer.child_type == b.data.pointer.child_type &&
a.data.pointer.is_const == b.data.pointer.is_const &&
a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
a.data.pointer.bit_offset == b.data.pointer.bit_offset &&
a.data.pointer.unaligned_bit_count == b.data.pointer.unaligned_bit_count;
case TypeTableEntryIdArray:
return a.data.array.child_type == b.data.array.child_type &&
a.data.array.size == b.data.array.size;
case TypeTableEntryIdInt:
return a.data.integer.is_signed == b.data.integer.is_signed &&
a.data.integer.bit_count == b.data.integer.bit_count;
}
zig_unreachable();
}
uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
switch (x.id) {
case ZigLLVMFnIdCtz:
return x.data.ctz.bit_count * 810453934;
case ZigLLVMFnIdClz:
return x.data.clz.bit_count * 2428952817;
case ZigLLVMFnIdOverflowArithmetic:
return (x.data.overflow_arithmetic.bit_count * 87135777) +
(x.data.overflow_arithmetic.add_sub_mul * 31640542) +
(x.data.overflow_arithmetic.is_signed ? 1062315172 : 314955820);
}
zig_unreachable();
}
bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
if (a.id != b.id)
return false;
switch (a.id) {
case ZigLLVMFnIdCtz:
return a.data.ctz.bit_count == b.data.ctz.bit_count;
case ZigLLVMFnIdClz:
return a.data.clz.bit_count == b.data.clz.bit_count;
case ZigLLVMFnIdOverflowArithmetic:
return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
(a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
(a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed);
}
zig_unreachable();
}
ConstParent *get_const_val_parent(ConstExprValue *value) {
assert(value->type);
TypeTableEntry *canon_type = get_underlying_type(value->type);
if (canon_type->id == TypeTableEntryIdArray) {
return &value->data.x_array.parent;
} else if (canon_type->id == TypeTableEntryIdStruct) {
return &value->data.x_struct.parent;
}
return nullptr;
}