zig/src/analyze.cpp

7260 lines
291 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "analyze.hpp"
#include "ast_render.hpp"
#include "config.h"
#include "error.hpp"
#include "ir.hpp"
#include "ir_print.hpp"
#include "os.hpp"
#include "parser.hpp"
#include "softfloat.hpp"
#include "zig_llvm.h"
static const size_t default_backward_branch_quota = 1000;
static Error resolve_struct_type(CodeGen *g, ZigType *struct_type);
static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type);
static Error ATTRIBUTE_MUST_USE resolve_struct_alignment(CodeGen *g, ZigType *struct_type);
static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type);
static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
static Error ATTRIBUTE_MUST_USE resolve_union_alignment(CodeGen *g, ZigType *union_type);
static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status);
static bool is_top_level_struct(ZigType *import) {
return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr;
}
static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ZigType *owner, Token *token, Buf *msg) {
assert(is_top_level_struct(owner));
RootStruct *root_struct = owner->data.structure.root_struct;
ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
root_struct->source_code, root_struct->line_offsets, msg);
err_msg_add_note(parent_msg, err);
return err;
}
ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg) {
assert(is_top_level_struct(owner));
RootStruct *root_struct = owner->data.structure.root_struct;
ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
root_struct->source_code, root_struct->line_offsets, msg);
g->errors.append(err);
return err;
}
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
Token fake_token;
fake_token.start_line = node->line;
fake_token.start_column = node->column;
return add_token_error(g, node->owner, &fake_token, msg);
}
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
Token fake_token;
fake_token.start_line = node->line;
fake_token.start_column = node->column;
return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
}
ZigType *new_type_table_entry(ZigTypeId id) {
ZigType *entry = allocate<ZigType>(1);
entry->id = id;
return entry;
}
static ScopeDecls **get_container_scope_ptr(ZigType *type_entry) {
if (type_entry->id == ZigTypeIdStruct) {
return &type_entry->data.structure.decls_scope;
} else if (type_entry->id == ZigTypeIdEnum) {
return &type_entry->data.enumeration.decls_scope;
} else if (type_entry->id == ZigTypeIdUnion) {
return &type_entry->data.unionation.decls_scope;
}
zig_unreachable();
}
ScopeDecls *get_container_scope(ZigType *type_entry) {
return *get_container_scope_ptr(type_entry);
}
void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) {
dest->codegen = g;
dest->id = id;
dest->source_node = source_node;
dest->parent = parent;
}
static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
ZigType *import, Buf *bare_name)
{
assert(node == nullptr || node->type == NodeTypeContainerDecl || node->type == NodeTypeFnCallExpr);
ScopeDecls *scope = allocate<ScopeDecls>(1);
init_scope(g, &scope->base, ScopeIdDecls, node, parent);
scope->decl_table.init(4);
scope->container_type = container_type;
scope->import = import;
scope->bare_name = bare_name;
return scope;
}
ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent) {
assert(node->type == NodeTypeBlock);
ScopeBlock *scope = allocate<ScopeBlock>(1);
init_scope(g, &scope->base, ScopeIdBlock, node, parent);
scope->name = node->data.block.name;
return scope;
}
ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent) {
assert(node->type == NodeTypeDefer);
ScopeDefer *scope = allocate<ScopeDefer>(1);
init_scope(g, &scope->base, ScopeIdDefer, node, parent);
return scope;
}
ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
assert(node->type == NodeTypeDefer);
ScopeDeferExpr *scope = allocate<ScopeDeferExpr>(1);
init_scope(g, &scope->base, ScopeIdDeferExpr, node, parent);
return scope;
}
Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) {
ScopeVarDecl *scope = allocate<ScopeVarDecl>(1);
init_scope(g, &scope->base, ScopeIdVarDecl, node, parent);
scope->var = var;
return &scope->base;
}
ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent) {
assert(node->type == NodeTypeFnCallExpr);
ScopeCImport *scope = allocate<ScopeCImport>(1);
init_scope(g, &scope->base, ScopeIdCImport, node, parent);
buf_resize(&scope->buf, 0);
return scope;
}
ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent) {
ScopeLoop *scope = allocate<ScopeLoop>(1);
init_scope(g, &scope->base, ScopeIdLoop, node, parent);
if (node->type == NodeTypeWhileExpr) {
scope->name = node->data.while_expr.name;
} else if (node->type == NodeTypeForExpr) {
scope->name = node->data.for_expr.name;
} else {
zig_unreachable();
}
return scope;
}
Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime) {
ScopeRuntime *scope = allocate<ScopeRuntime>(1);
scope->is_comptime = is_comptime;
init_scope(g, &scope->base, ScopeIdRuntime, node, parent);
return &scope->base;
}
ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
assert(node->type == NodeTypeSuspend);
ScopeSuspend *scope = allocate<ScopeSuspend>(1);
init_scope(g, &scope->base, ScopeIdSuspend, node, parent);
return scope;
}
ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry) {
ScopeFnDef *scope = allocate<ScopeFnDef>(1);
init_scope(g, &scope->base, ScopeIdFnDef, node, parent);
scope->fn_entry = fn_entry;
return scope;
}
Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
ScopeCompTime *scope = allocate<ScopeCompTime>(1);
init_scope(g, &scope->base, ScopeIdCompTime, node, parent);
return &scope->base;
}
Scope *create_coro_prelude_scope(CodeGen *g, AstNode *node, Scope *parent) {
ScopeCoroPrelude *scope = allocate<ScopeCoroPrelude>(1);
init_scope(g, &scope->base, ScopeIdCoroPrelude, node, parent);
return &scope->base;
}
ZigType *get_scope_import(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
assert(is_top_level_struct(decls_scope->import));
return decls_scope->import;
}
scope = scope->parent;
}
zig_unreachable();
}
static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope,
Buf *bare_name)
{
ZigType *entry = new_type_table_entry(id);
*get_container_scope_ptr(entry) = create_decls_scope(g, source_node, parent_scope, entry,
get_scope_import(parent_scope), bare_name);
return entry;
}
static uint8_t bits_needed_for_unsigned(uint64_t x) {
if (x == 0) {
return 0;
}
uint8_t base = log2_u64(x);
uint64_t upper = (((uint64_t)1) << base) - 1;
return (upper >= x) ? base : (base + 1);
}
AstNode *type_decl_node(ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdStruct:
return type_entry->data.structure.decl_node;
case ZigTypeIdEnum:
return type_entry->data.enumeration.decl_node;
case ZigTypeIdUnion:
return type_entry->data.unionation.decl_node;
case ZigTypeIdOpaque:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
case ZigTypeIdVector:
return nullptr;
}
zig_unreachable();
}
bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdStruct:
return type_entry->data.structure.resolve_status >= status;
case ZigTypeIdUnion:
return type_entry->data.unionation.resolve_status >= status;
case ZigTypeIdEnum:
switch (status) {
case ResolveStatusUnstarted:
return true;
case ResolveStatusInvalid:
zig_unreachable();
case ResolveStatusZeroBitsKnown:
return type_entry->data.enumeration.zero_bits_known;
case ResolveStatusAlignmentKnown:
return type_entry->data.enumeration.zero_bits_known;
case ResolveStatusSizeKnown:
return type_entry->data.enumeration.complete;
case ResolveStatusLLVMFwdDecl:
case ResolveStatusLLVMFull:
return type_entry->llvm_di_type != nullptr;
}
zig_unreachable();
case ZigTypeIdOpaque:
return status < ResolveStatusSizeKnown;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
case ZigTypeIdVector:
return true;
}
zig_unreachable();
}
bool type_is_complete(ZigType *type_entry) {
return type_is_resolved(type_entry, ResolveStatusSizeKnown);
}
uint64_t type_size(CodeGen *g, ZigType *type_entry) {
assert(type_is_resolved(type_entry, ResolveStatusSizeKnown));
return type_entry->abi_size;
}
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) {
assert(type_is_resolved(type_entry, ResolveStatusSizeKnown));
return type_entry->size_in_bits;
}
uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) {
assert(type_is_resolved(type_entry, ResolveStatusAlignmentKnown));
return type_entry->abi_align;
}
static bool is_slice(ZigType *type) {
return type->id == ZigTypeIdStruct && type->data.structure.is_slice;
}
ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
return get_int_type(g, false, bits_needed_for_unsigned(x));
}
ZigType *get_promise_type(CodeGen *g, ZigType *result_type) {
if (result_type != nullptr && result_type->promise_parent != nullptr) {
return result_type->promise_parent;
} else if (result_type == nullptr && g->builtin_types.entry_promise != nullptr) {
return g->builtin_types.entry_promise;
}
ZigType *entry = new_type_table_entry(ZigTypeIdPromise);
entry->abi_size = g->builtin_types.entry_usize->abi_size;
entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
entry->abi_align = g->builtin_types.entry_usize->abi_align;
entry->data.promise.result_type = result_type;
buf_init_from_str(&entry->name, "promise");
if (result_type != nullptr) {
buf_appendf(&entry->name, "->%s", buf_ptr(&result_type->name));
}
if (result_type != nullptr) {
result_type->promise_parent = entry;
} else if (result_type == nullptr) {
g->builtin_types.entry_promise = entry;
}
return entry;
}
static const char *ptr_len_to_star_str(PtrLen ptr_len) {
switch (ptr_len) {
case PtrLenSingle:
return "*";
case PtrLenUnknown:
return "[*]";
case PtrLenC:
return "[*c]";
}
zig_unreachable();
}
ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero)
{
assert(ptr_len != PtrLenC || allow_zero);
assert(!type_is_invalid(child_type));
assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
if (byte_alignment != 0) {
uint32_t abi_alignment = get_abi_alignment(g, child_type);
if (byte_alignment == abi_alignment)
byte_alignment = 0;
}
if (host_int_bytes != 0) {
uint32_t child_type_bits = type_size_bits(g, child_type);
if (host_int_bytes * 8 == child_type_bits) {
assert(bit_offset_in_host == 0);
host_int_bytes = 0;
}
}
TypeId type_id = {};
ZigType **parent_pointer = nullptr;
if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || allow_zero) {
type_id.id = ZigTypeIdPointer;
type_id.data.pointer.child_type = child_type;
type_id.data.pointer.is_const = is_const;
type_id.data.pointer.is_volatile = is_volatile;
type_id.data.pointer.alignment = byte_alignment;
type_id.data.pointer.bit_offset_in_host = bit_offset_in_host;
type_id.data.pointer.host_int_bytes = host_int_bytes;
type_id.data.pointer.ptr_len = ptr_len;
type_id.data.pointer.allow_zero = allow_zero;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry)
return existing_entry->value;
} else {
assert(bit_offset_in_host == 0);
parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)];
if (*parent_pointer) {
assert((*parent_pointer)->data.pointer.explicit_alignment == 0);
return *parent_pointer;
}
}
assert(type_is_resolved(child_type, ResolveStatusZeroBitsKnown));
ZigType *entry = new_type_table_entry(ZigTypeIdPointer);
const char *star_str = ptr_len_to_star_str(ptr_len);
const char *const_str = is_const ? "const " : "";
const char *volatile_str = is_volatile ? "volatile " : "";
const char *allow_zero_str;
if (ptr_len == PtrLenC) {
assert(allow_zero);
allow_zero_str = "";
} else {
allow_zero_str = allow_zero ? "allowzero " : "";
}
buf_resize(&entry->name, 0);
if (host_int_bytes == 0 && byte_alignment == 0) {
buf_appendf(&entry->name, "%s%s%s%s%s",
star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
} else if (host_int_bytes == 0) {
buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
} else if (byte_alignment == 0) {
buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str,
bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
buf_ptr(&child_type->name));
} else {
buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
buf_ptr(&child_type->name));
}
assert(child_type->id != ZigTypeIdInvalid);
if (type_has_bits(child_type)) {
entry->abi_size = g->builtin_types.entry_usize->abi_size;
entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
entry->abi_align = g->builtin_types.entry_usize->abi_align;
} else {
assert(byte_alignment == 0);
entry->abi_size = 0;
entry->size_in_bits = 0;
entry->abi_align = 0;
}
entry->data.pointer.ptr_len = ptr_len;
entry->data.pointer.child_type = child_type;
entry->data.pointer.is_const = is_const;
entry->data.pointer.is_volatile = is_volatile;
entry->data.pointer.explicit_alignment = byte_alignment;
entry->data.pointer.bit_offset_in_host = bit_offset_in_host;
entry->data.pointer.host_int_bytes = host_int_bytes;
entry->data.pointer.allow_zero = allow_zero;
if (parent_pointer) {
*parent_pointer = entry;
} else {
g->type_table.put(type_id, entry);
}
return entry;
}
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false);
}
ZigType *get_promise_frame_type(CodeGen *g, ZigType *return_type) {
if (return_type->promise_frame_parent != nullptr) {
return return_type->promise_frame_parent;
}
ZigType *atomic_state_type = g->builtin_types.entry_usize;
ZigType *result_ptr_type = get_pointer_to_type(g, return_type, false);
ZigList<const char *> field_names = {};
field_names.append(ATOMIC_STATE_FIELD_NAME);
field_names.append(RESULT_FIELD_NAME);
field_names.append(RESULT_PTR_FIELD_NAME);
if (g->have_err_ret_tracing) {
field_names.append(ERR_RET_TRACE_PTR_FIELD_NAME);
field_names.append(ERR_RET_TRACE_FIELD_NAME);
field_names.append(RETURN_ADDRESSES_FIELD_NAME);
}
ZigList<ZigType *> field_types = {};
field_types.append(atomic_state_type);
field_types.append(return_type);
field_types.append(result_ptr_type);
if (g->have_err_ret_tracing) {
field_types.append(get_ptr_to_stack_trace_type(g));
field_types.append(g->stack_trace_type);
field_types.append(get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count));
}
assert(field_names.length == field_types.length);
Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name));
ZigType *entry = get_struct_type(g, buf_ptr(name), field_names.items, field_types.items, field_names.length);
return_type->promise_frame_parent = entry;
return entry;
}
ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
if (child_type->optional_parent != nullptr) {
return child_type->optional_parent;
}
assert(type_is_resolved(child_type, ResolveStatusSizeKnown));
ZigType *entry = new_type_table_entry(ZigTypeIdOptional);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
if (!type_has_bits(child_type)) {
entry->size_in_bits = g->builtin_types.entry_bool->size_in_bits;
entry->abi_size = g->builtin_types.entry_bool->abi_size;
entry->abi_align = g->builtin_types.entry_bool->abi_align;
} else if (type_is_nonnull_ptr(child_type) || child_type->id == ZigTypeIdErrorSet) {
// This is an optimization but also is necessary for calling C
// functions where all pointers are optional pointers.
// Function types are technically pointers.
entry->size_in_bits = child_type->size_in_bits;
entry->abi_size = child_type->abi_size;
entry->abi_align = child_type->abi_align;
} else {
// This value only matters if the type is legal in a packed struct, which is not
// true for optional types which did not fit the above 2 categories (zero bit child type,
// or nonnull ptr child type, or error set child type).
entry->size_in_bits = child_type->size_in_bits + 1;
// We're going to make a struct with the child type as the first field,
// and a bool as the second. Since the child type's abi alignment is guaranteed
// to be >= the bool's abi size (1 byte), the added size is exactly equal to the
// child type's ABI alignment.
assert(child_type->abi_align >= g->builtin_types.entry_bool->abi_size);
entry->abi_align = child_type->abi_align;
entry->abi_size = child_type->abi_size + child_type->abi_align;
}
entry->data.maybe.child_type = child_type;
child_type->optional_parent = entry;
return entry;
}
static size_t align_forward(size_t addr, size_t alignment) {
return (addr + alignment - 1) & ~(alignment - 1);
}
static size_t next_field_offset(size_t offset, size_t align_from_zero, size_t field_size, size_t next_field_align) {
// Convert offset to a pretend address which has the specified alignment.
size_t addr = offset + align_from_zero;
// March the address forward to respect the field alignment.
size_t aligned_addr = align_forward(addr + field_size, next_field_align);
// Convert back from pretend address to offset.
return aligned_addr - align_from_zero;
}
ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type) {
assert(err_set_type->id == ZigTypeIdErrorSet);
assert(!type_is_invalid(payload_type));
TypeId type_id = {};
type_id.id = ZigTypeIdErrorUnion;
type_id.data.error_union.err_set_type = err_set_type;
type_id.data.error_union.payload_type = payload_type;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry) {
return existing_entry->value;
}
ZigType *entry = new_type_table_entry(ZigTypeIdErrorUnion);
assert(type_is_resolved(payload_type, ResolveStatusSizeKnown));
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
entry->data.error_union.err_set_type = err_set_type;
entry->data.error_union.payload_type = payload_type;
if (!type_has_bits(payload_type)) {
if (type_has_bits(err_set_type)) {
entry->size_in_bits = err_set_type->size_in_bits;
entry->abi_size = err_set_type->abi_size;
entry->abi_align = err_set_type->abi_align;
} else {
entry->size_in_bits = 0;
entry->abi_size = 0;
entry->abi_align = 0;
}
} else if (!type_has_bits(err_set_type)) {
entry->size_in_bits = payload_type->size_in_bits;
entry->abi_size = payload_type->abi_size;
entry->abi_align = payload_type->abi_align;
} else {
entry->abi_align = max(err_set_type->abi_align, payload_type->abi_align);
size_t field_sizes[2];
size_t field_aligns[2];
field_sizes[err_union_err_index] = err_set_type->abi_size;
field_aligns[err_union_err_index] = err_set_type->abi_align;
field_sizes[err_union_payload_index] = payload_type->abi_size;
field_aligns[err_union_payload_index] = payload_type->abi_align;
size_t field2_offset = next_field_offset(0, entry->abi_align, field_sizes[0], field_aligns[1]);
entry->abi_size = next_field_offset(field2_offset, entry->abi_align, field_sizes[1], entry->abi_align);
entry->size_in_bits = entry->abi_size * 8;
}
g->type_table.put(type_id, entry);
return entry;
}
ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size) {
TypeId type_id = {};
type_id.id = ZigTypeIdArray;
type_id.data.array.child_type = child_type;
type_id.data.array.size = array_size;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry) {
return existing_entry->value;
}
assert(type_is_resolved(child_type, ResolveStatusSizeKnown));
ZigType *entry = new_type_table_entry(ZigTypeIdArray);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[%" ZIG_PRI_u64 "]%s", array_size, buf_ptr(&child_type->name));
entry->size_in_bits = child_type->size_in_bits * array_size;
entry->abi_align = child_type->abi_align;
entry->abi_size = child_type->abi_size * array_size;
entry->data.array.child_type = child_type;
entry->data.array.len = array_size;
g->type_table.put(type_id, entry);
return entry;
}
ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
assert(ptr_type->id == ZigTypeIdPointer);
assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown);
ZigType **parent_pointer = &ptr_type->data.pointer.slice_parent;
if (*parent_pointer) {
return *parent_pointer;
}
ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
// replace the & with [] to go from a ptr type name to a slice type name
buf_resize(&entry->name, 0);
size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3;
buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset);
unsigned element_count = 2;
Buf *ptr_field_name = buf_create_from_str("ptr");
Buf *len_field_name = buf_create_from_str("len");
entry->data.structure.resolve_status = ResolveStatusSizeKnown;
entry->data.structure.layout = ContainerLayoutAuto;
entry->data.structure.is_slice = true;
entry->data.structure.src_field_count = element_count;
entry->data.structure.gen_field_count = element_count;
entry->data.structure.fields = allocate<TypeStructField>(element_count);
entry->data.structure.fields_by_name.init(element_count);
entry->data.structure.fields[slice_ptr_index].name = ptr_field_name;
entry->data.structure.fields[slice_ptr_index].type_entry = ptr_type;
entry->data.structure.fields[slice_ptr_index].src_index = slice_ptr_index;
entry->data.structure.fields[slice_ptr_index].gen_index = 0;
entry->data.structure.fields[slice_len_index].name = len_field_name;
entry->data.structure.fields[slice_len_index].type_entry = g->builtin_types.entry_usize;
entry->data.structure.fields[slice_len_index].src_index = slice_len_index;
entry->data.structure.fields[slice_len_index].gen_index = 1;
entry->data.structure.fields_by_name.put(ptr_field_name, &entry->data.structure.fields[slice_ptr_index]);
entry->data.structure.fields_by_name.put(len_field_name, &entry->data.structure.fields[slice_len_index]);
switch (type_requires_comptime(g, ptr_type)) {
case ReqCompTimeInvalid:
zig_unreachable();
case ReqCompTimeNo:
break;
case ReqCompTimeYes:
entry->data.structure.requires_comptime = true;
}
if (!type_has_bits(ptr_type)) {
entry->data.structure.gen_field_count = 1;
entry->data.structure.fields[slice_ptr_index].gen_index = SIZE_MAX;
entry->data.structure.fields[slice_len_index].gen_index = 0;
}
ZigType *child_type = ptr_type->data.pointer.child_type;
if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero)
{
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenUnknown, 0, 0, 0, false);
ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
entry->size_in_bits = peer_slice_type->size_in_bits;
entry->abi_size = peer_slice_type->abi_size;
entry->abi_align = peer_slice_type->abi_align;
*parent_pointer = entry;
return entry;
}
if (type_has_bits(ptr_type)) {
entry->size_in_bits = ptr_type->size_in_bits + g->builtin_types.entry_usize->size_in_bits;
entry->abi_size = ptr_type->abi_size + g->builtin_types.entry_usize->abi_size;
entry->abi_align = ptr_type->abi_align;
} else {
entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
entry->abi_size = g->builtin_types.entry_usize->abi_size;
entry->abi_align = g->builtin_types.entry_usize->abi_align;
}
*parent_pointer = entry;
return entry;
}
ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name) {
ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);
buf_init_from_str(&entry->name, full_name);
ZigType *import = scope ? get_scope_import(scope) : nullptr;
unsigned line = source_node ? (unsigned)(source_node->line + 1) : 0;
entry->llvm_type = LLVMInt8Type();
entry->llvm_di_type = ZigLLVMCreateDebugForwardDeclType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), full_name,
import ? ZigLLVMFileToScope(import->data.structure.root_struct->di_file) : nullptr,
import ? import->data.structure.root_struct->di_file : nullptr,
line);
entry->data.opaque.bare_name = bare_name;
// The actual size is unknown, but the value must not be 0 because that
// is how type_has_bits is determined.
entry->abi_size = SIZE_MAX;
entry->size_in_bits = SIZE_MAX;
entry->abi_align = 1;
return entry;
}
ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) {
ZigType *fn_type = fn_entry->type_entry;
assert(fn_type->id == ZigTypeIdFn);
if (fn_type->data.fn.bound_fn_parent)
return fn_type->data.fn.bound_fn_parent;
ZigType *bound_fn_type = new_type_table_entry(ZigTypeIdBoundFn);
bound_fn_type->data.bound_fn.fn_type = fn_type;
buf_resize(&bound_fn_type->name, 0);
buf_appendf(&bound_fn_type->name, "(bound %s)", buf_ptr(&fn_type->name));
fn_type->data.fn.bound_fn_parent = bound_fn_type;
return bound_fn_type;
}
const char *calling_convention_name(CallingConvention cc) {
switch (cc) {
case CallingConventionUnspecified: return "undefined";
case CallingConventionC: return "ccc";
case CallingConventionCold: return "coldcc";
case CallingConventionNaked: return "nakedcc";
case CallingConventionStdcall: return "stdcallcc";
case CallingConventionAsync: return "async";
}
zig_unreachable();
}
static const char *calling_convention_fn_type_str(CallingConvention cc) {
switch (cc) {
case CallingConventionUnspecified: return "";
case CallingConventionC: return "extern ";
case CallingConventionCold: return "coldcc ";
case CallingConventionNaked: return "nakedcc ";
case CallingConventionStdcall: return "stdcallcc ";
case CallingConventionAsync: return "async ";
}
zig_unreachable();
}
bool calling_convention_allows_zig_types(CallingConvention cc) {
switch (cc) {
case CallingConventionUnspecified:
case CallingConventionAsync:
return true;
case CallingConventionC:
case CallingConventionCold:
case CallingConventionNaked:
case CallingConventionStdcall:
return false;
}
zig_unreachable();
}
ZigType *get_ptr_to_stack_trace_type(CodeGen *g) {
if (g->stack_trace_type == nullptr) {
ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace");
assert(stack_trace_type_val->type->id == ZigTypeIdMetaType);
g->stack_trace_type = stack_trace_type_val->data.x_type;
assertNoError(type_resolve(g, g->stack_trace_type, ResolveStatusZeroBitsKnown));
g->ptr_to_stack_trace_type = get_pointer_to_type(g, g->stack_trace_type, false);
}
return g->ptr_to_stack_trace_type;
}
bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
if (fn_type_id->cc == CallingConventionUnspecified) {
return handle_is_ptr(fn_type_id->return_type);
}
if (fn_type_id->cc != CallingConventionC) {
return false;
}
if (type_is_c_abi_int(g, fn_type_id->return_type)) {
return false;
}
if (g->zig_target->arch == ZigLLVM_x86_64) {
X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
return abi_class == X64CABIClass_MEMORY;
} else if (target_is_arm(g->zig_target)) {
return type_size(g, fn_type_id->return_type) > 16;
}
zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481");
}
ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
Error err;
auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
if (table_entry) {
return table_entry->value;
}
if (fn_type_id->return_type != nullptr) {
if ((err = ensure_complete_type(g, fn_type_id->return_type)))
return g->builtin_types.entry_invalid;
assert(fn_type_id->return_type->id != ZigTypeIdOpaque);
} else {
zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447");
}
ZigType *fn_type = new_type_table_entry(ZigTypeIdFn);
fn_type->data.fn.fn_type_id = *fn_type_id;
// populate the name of the type
buf_resize(&fn_type->name, 0);
if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
assert(fn_type_id->async_allocator_type != nullptr);
buf_appendf(&fn_type->name, "async<%s> ", buf_ptr(&fn_type_id->async_allocator_type->name));
} else {
const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
buf_appendf(&fn_type->name, "%s", cc_str);
}
buf_appendf(&fn_type->name, "fn(");
for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
ZigType *param_type = param_info->type;
const char *comma = (i == 0) ? "" : ", ";
const char *noalias_str = param_info->is_noalias ? "noalias " : "";
buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(&param_type->name));
}
if (fn_type_id->is_var_args) {
const char *comma = (fn_type_id->param_count == 0) ? "" : ", ";
buf_appendf(&fn_type->name, "%s...", comma);
}
buf_appendf(&fn_type->name, ")");
if (fn_type_id->alignment != 0) {
buf_appendf(&fn_type->name, " align(%" PRIu32 ")", fn_type_id->alignment);
}
buf_appendf(&fn_type->name, " %s", buf_ptr(&fn_type_id->return_type->name));
// The fn_type is a pointer; not to be confused with the raw function type.
fn_type->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
fn_type->abi_size = g->builtin_types.entry_usize->abi_size;
fn_type->abi_align = g->builtin_types.entry_usize->abi_align;
g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);
return fn_type;
}
static ZigTypeId container_to_type(ContainerKind kind) {
switch (kind) {
case ContainerKindStruct:
return ZigTypeIdStruct;
case ContainerKindEnum:
return ZigTypeIdEnum;
case ContainerKindUnion:
return ZigTypeIdUnion;
}
zig_unreachable();
}
// This is like get_partial_container_type except it's for the implicit root struct of files.
static ZigType *get_root_container_type(CodeGen *g, const char *full_name, Buf *bare_name,
RootStruct *root_struct)
{
ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
entry->data.structure.decls_scope = create_decls_scope(g, nullptr, nullptr, entry, entry, bare_name);
entry->data.structure.root_struct = root_struct;
entry->data.structure.layout = ContainerLayoutAuto;
buf_init_from_str(&entry->name, full_name);
return entry;
}
ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout)
{
ZigTypeId type_id = container_to_type(kind);
ZigType *entry = new_container_type_entry(g, type_id, decl_node, scope, bare_name);
switch (kind) {
case ContainerKindStruct:
entry->data.structure.decl_node = decl_node;
entry->data.structure.layout = layout;
break;
case ContainerKindEnum:
entry->data.enumeration.decl_node = decl_node;
entry->data.enumeration.layout = layout;
break;
case ContainerKindUnion:
entry->data.unionation.decl_node = decl_node;
entry->data.unionation.layout = layout;
break;
}
buf_init_from_str(&entry->name, full_name);
return entry;
}
static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
Buf *type_name)
{
size_t backward_branch_count = 0;
size_t backward_branch_quota = default_backward_branch_quota;
return ir_eval_const_value(g, scope, node, type_entry,
&backward_branch_count, &backward_branch_quota,
nullptr, nullptr, node, type_name, nullptr, nullptr);
}
ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
ConstExprValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr);
if (type_is_invalid(result->type))
return g->builtin_types.entry_invalid;
assert(result->special != ConstValSpecialRuntime);
return result->data.x_type;
}
ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
ZigType *fn_type = new_type_table_entry(ZigTypeIdFn);
buf_resize(&fn_type->name, 0);
if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
const char *async_allocator_type_str = (fn_type->data.fn.fn_type_id.async_allocator_type == nullptr) ?
"var" : buf_ptr(&fn_type_id->async_allocator_type->name);
buf_appendf(&fn_type->name, "async(%s) ", async_allocator_type_str);
} else {
const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
buf_appendf(&fn_type->name, "%s", cc_str);
}
buf_appendf(&fn_type->name, "fn(");
size_t i = 0;
for (; i < fn_type_id->next_param_index; i += 1) {
const char *comma_str = (i == 0) ? "" : ",";
buf_appendf(&fn_type->name, "%s%s", comma_str,
buf_ptr(&fn_type_id->param_info[i].type->name));
}
for (; i < fn_type_id->param_count; i += 1) {
const char *comma_str = (i == 0) ? "" : ",";
buf_appendf(&fn_type->name, "%svar", comma_str);
}
buf_appendf(&fn_type->name, ")var");
fn_type->data.fn.fn_type_id = *fn_type_id;
fn_type->data.fn.is_generic = true;
fn_type->abi_size = 0;
fn_type->size_in_bits = 0;
fn_type->abi_align = 0;
return fn_type;
}
void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
if (fn_proto->cc == CallingConventionUnspecified) {
bool extern_abi = fn_proto->is_extern || fn_proto->is_export;
fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified;
} else {
fn_type_id->cc = fn_proto->cc;
}
fn_type_id->param_count = fn_proto->params.length;
fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc);
fn_type_id->next_param_index = 0;
fn_type_id->is_var_args = fn_proto->is_var_args;
}
static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) {
ConstExprValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr);
if (type_is_invalid(align_result->type))
return false;
uint32_t align_bytes = bigint_as_unsigned(&align_result->data.x_bigint);
if (align_bytes == 0) {
add_node_error(g, node, buf_sprintf("alignment must be >= 1"));
return false;
}
if (!is_power_of_2(align_bytes)) {
add_node_error(g, node, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
return false;
}
*result = align_bytes;
return true;
}
static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, 0, 0, 0, false);
ZigType *str_type = get_slice_type(g, ptr_type);
ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr);
if (type_is_invalid(result_val->type))
return false;
ConstExprValue *ptr_field = &result_val->data.x_struct.fields[slice_ptr_index];
ConstExprValue *len_field = &result_val->data.x_struct.fields[slice_len_index];
assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
if (array_val->data.x_array.special == ConstArraySpecialBuf) {
*out_buffer = array_val->data.x_array.data.s_buf;
return true;
}
expand_undef_array(g, array_val);
size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
Buf *result = buf_alloc();
buf_resize(result, len);
for (size_t i = 0; i < len; i += 1) {
size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
ConstExprValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index];
if (char_val->special == ConstValSpecialUndef) {
add_node_error(g, node, buf_sprintf("use of undefined value"));
return false;
}
uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint);
assert(big_c <= UINT8_MAX);
uint8_t c = (uint8_t)big_c;
buf_ptr(result)[i] = c;
}
*out_buffer = result;
return true;
}
static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry,
AstNode *source_node)
{
Error err;
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
add_node_error(g, source_node,
buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
buf_ptr(&type_entry->name)));
return ErrorSemanticAnalyzeFail;
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdFn:
case ZigTypeIdVector:
return ErrorNone;
case ZigTypeIdArray: {
ZigType *elem_type = type_entry->data.array.child_type;
if ((err = emit_error_unless_type_allowed_in_packed_struct(g, elem_type, source_node)))
return err;
// TODO revisit this when doing https://github.com/ziglang/zig/issues/1512
if (type_size(g, type_entry) * 8 == type_size_bits(g, type_entry))
return ErrorNone;
add_node_error(g, source_node,
buf_sprintf("array of '%s' not allowed in packed struct due to padding bits",
buf_ptr(&elem_type->name)));
return ErrorSemanticAnalyzeFail;
}
case ZigTypeIdStruct:
switch (type_entry->data.structure.layout) {
case ContainerLayoutPacked:
case ContainerLayoutExtern:
return ErrorNone;
case ContainerLayoutAuto:
add_node_error(g, source_node,
buf_sprintf("non-packed, non-extern struct '%s' not allowed in packed struct; no guaranteed in-memory representation",
buf_ptr(&type_entry->name)));
return ErrorSemanticAnalyzeFail;
}
zig_unreachable();
case ZigTypeIdUnion:
switch (type_entry->data.unionation.layout) {
case ContainerLayoutPacked:
case ContainerLayoutExtern:
return ErrorNone;
case ContainerLayoutAuto:
add_node_error(g, source_node,
buf_sprintf("non-packed, non-extern union '%s' not allowed in packed struct; no guaranteed in-memory representation",
buf_ptr(&type_entry->name)));
return ErrorSemanticAnalyzeFail;
}
zig_unreachable();
case ZigTypeIdOptional:
if (get_codegen_ptr_type(type_entry) != nullptr) {
return ErrorNone;
} else {
add_node_error(g, source_node,
buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
buf_ptr(&type_entry->name)));
return ErrorSemanticAnalyzeFail;
}
case ZigTypeIdEnum: {
AstNode *decl_node = type_entry->data.enumeration.decl_node;
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
return ErrorNone;
}
ErrorMsg *msg = add_node_error(g, source_node,
buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation",
buf_ptr(&type_entry->name)));
add_error_note(g, msg, decl_node,
buf_sprintf("enum declaration does not specify an integer tag type"));
return ErrorSemanticAnalyzeFail;
}
}
zig_unreachable();
}
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
case ZigTypeIdVoid:
return false;
case ZigTypeIdOpaque:
case ZigTypeIdUnreachable:
case ZigTypeIdBool:
return true;
case ZigTypeIdInt:
switch (type_entry->data.integral.bit_count) {
case 8:
case 16:
case 32:
case 64:
case 128:
return true;
default:
return false;
}
case ZigTypeIdVector:
return type_allowed_in_extern(g, type_entry->data.vector.elem_type);
case ZigTypeIdFloat:
return true;
case ZigTypeIdArray:
return type_allowed_in_extern(g, type_entry->data.array.child_type);
case ZigTypeIdFn:
return type_entry->data.fn.fn_type_id.cc == CallingConventionC ||
type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall;
case ZigTypeIdPointer:
if (type_size(g, type_entry) == 0)
return false;
return true;
case ZigTypeIdStruct:
return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;
case ZigTypeIdOptional:
{
ZigType *child_type = type_entry->data.maybe.child_type;
if (child_type->id != ZigTypeIdPointer && child_type->id != ZigTypeIdFn) {
return false;
}
return type_allowed_in_extern(g, child_type);
}
case ZigTypeIdEnum:
return type_entry->data.enumeration.layout == ContainerLayoutExtern || type_entry->data.enumeration.layout == ContainerLayoutPacked;
case ZigTypeIdUnion:
return type_entry->data.unionation.layout == ContainerLayoutExtern || type_entry->data.unionation.layout == ContainerLayoutPacked;
}
zig_unreachable();
}
ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) {
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
buf_resize(&err_set_type->name, 0);
buf_appendf(&err_set_type->name, "@typeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name));
err_set_type->data.error_set.err_count = 0;
err_set_type->data.error_set.errors = nullptr;
err_set_type->data.error_set.infer_fn = fn_entry;
err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits;
err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
return err_set_type;
}
static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, ZigFn *fn_entry) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
Error err;
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index);
assert(param_node->type == NodeTypeParamDecl);
bool param_is_comptime = param_node->data.param_decl.is_inline;
bool param_is_var_args = param_node->data.param_decl.is_var_args;
if (param_is_comptime) {
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
add_node_error(g, param_node,
buf_sprintf("comptime parameter not allowed in function with calling convention '%s'",
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
if (param_node->data.param_decl.type != nullptr) {
ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
if (type_is_invalid(type_entry)) {
return g->builtin_types.entry_invalid;
}
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
param_info->type = type_entry;
param_info->is_noalias = param_node->data.param_decl.is_noalias;
fn_type_id.next_param_index += 1;
}
return get_generic_fn_type(g, &fn_type_id);
} else if (param_is_var_args) {
if (fn_type_id.cc == CallingConventionC) {
fn_type_id.param_count = fn_type_id.next_param_index;
continue;
} else if (calling_convention_allows_zig_types(fn_type_id.cc)) {
return get_generic_fn_type(g, &fn_type_id);
} else {
add_node_error(g, param_node,
buf_sprintf("var args not allowed in function with calling convention '%s'",
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
} else if (param_node->data.param_decl.var_token != nullptr) {
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
add_node_error(g, param_node,
buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'",
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
return get_generic_fn_type(g, &fn_type_id);
}
ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
if (type_is_invalid(type_entry)) {
return g->builtin_types.entry_invalid;
}
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
return g->builtin_types.entry_invalid;
if (!type_has_bits(type_entry)) {
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
}
if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, type_entry)) {
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&type_entry->name),
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name)));
return g->builtin_types.entry_invalid;
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdBoundFn:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
case ZigTypeIdVector:
switch (type_requires_comptime(g, type_entry)) {
case ReqCompTimeNo:
break;
case ReqCompTimeYes:
add_node_error(g, param_node->data.param_decl.type,
buf_sprintf("parameter of type '%s' must be declared comptime",
buf_ptr(&type_entry->name)));
return g->builtin_types.entry_invalid;
case ReqCompTimeInvalid:
return g->builtin_types.entry_invalid;
}
break;
}
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
param_info->type = type_entry;
param_info->is_noalias = param_node->data.param_decl.is_noalias;
}
if (fn_proto->align_expr != nullptr) {
if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &fn_type_id.alignment)) {
return g->builtin_types.entry_invalid;
}
}
if (fn_proto->return_var_token != nullptr) {
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
add_node_error(g, fn_proto->return_type,
buf_sprintf("return type 'var' not allowed in function with calling convention '%s'",
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
add_node_error(g, proto_node,
buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
return g->builtin_types.entry_invalid;
//return get_generic_fn_type(g, &fn_type_id);
}
ZigType *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type);
if (type_is_invalid(specified_return_type)) {
fn_type_id.return_type = g->builtin_types.entry_invalid;
return g->builtin_types.entry_invalid;
}
if (fn_proto->auto_err_set) {
ZigType *inferred_err_set_type = get_auto_err_set_type(g, fn_entry);
if ((err = type_resolve(g, specified_return_type, ResolveStatusSizeKnown)))
return g->builtin_types.entry_invalid;
fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type);
} else {
fn_type_id.return_type = specified_return_type;
}
if (!calling_convention_allows_zig_types(fn_type_id.cc) &&
fn_type_id.return_type->id != ZigTypeIdVoid &&
!type_allowed_in_extern(g, fn_type_id.return_type))
{
add_node_error(g, fn_proto->return_type,
buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&fn_type_id.return_type->name),
calling_convention_name(fn_type_id.cc)));
return g->builtin_types.entry_invalid;
}
switch (fn_type_id.return_type->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
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 ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdBoundFn:
case ZigTypeIdMetaType:
case ZigTypeIdUnreachable:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
case ZigTypeIdVector:
switch (type_requires_comptime(g, fn_type_id.return_type)) {
case ReqCompTimeInvalid:
return g->builtin_types.entry_invalid;
case ReqCompTimeYes:
return get_generic_fn_type(g, &fn_type_id);
case ReqCompTimeNo:
break;
}
break;
}
if (fn_type_id.cc == CallingConventionAsync) {
if (fn_proto->async_allocator_type == nullptr) {
return get_generic_fn_type(g, &fn_type_id);
}
fn_type_id.async_allocator_type = analyze_type_expr(g, child_scope, fn_proto->async_allocator_type);
if (type_is_invalid(fn_type_id.async_allocator_type)) {
return g->builtin_types.entry_invalid;
}
}
return get_fn_type(g, &fn_type_id);
}
bool type_is_invalid(ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
return true;
case ZigTypeIdStruct:
return type_entry->data.structure.resolve_status == ResolveStatusInvalid;
case ZigTypeIdUnion:
return type_entry->data.unionation.resolve_status == ResolveStatusInvalid;
case ZigTypeIdEnum:
return type_entry->data.enumeration.is_invalid;
default:
return false;
}
zig_unreachable();
}
ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
ZigType *field_types[], size_t field_count)
{
ZigType *struct_type = new_type_table_entry(ZigTypeIdStruct);
buf_init_from_str(&struct_type->name, type_name);
struct_type->data.structure.src_field_count = field_count;
struct_type->data.structure.gen_field_count = 0;
struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
struct_type->data.structure.fields_by_name.init(field_count);
size_t abi_align = 0;
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
field->name = buf_create_from_str(field_names[i]);
field->type_entry = field_types[i];
field->src_index = i;
if (type_has_bits(field->type_entry)) {
assert(type_is_resolved(field->type_entry, ResolveStatusSizeKnown));
if (field->type_entry->abi_align > abi_align) {
abi_align = field->type_entry->abi_align;
}
field->gen_index = struct_type->data.structure.gen_field_count;
struct_type->data.structure.gen_field_count += 1;
} else {
field->gen_index = SIZE_MAX;
}
auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field);
assert(prev_entry == nullptr);
}
size_t next_offset = 0;
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
if (field->gen_index == SIZE_MAX)
continue;
field->offset = next_offset;
size_t next_src_field_index = i + 1;
for (; next_src_field_index < field_count; next_src_field_index += 1) {
if (struct_type->data.structure.fields[next_src_field_index].gen_index != SIZE_MAX) {
break;
}
}
size_t next_abi_align = (next_src_field_index == field_count) ?
abi_align : struct_type->data.structure.fields[next_src_field_index].type_entry->abi_align;
next_offset = next_field_offset(next_offset, abi_align, field->type_entry->abi_size, next_abi_align);
}
struct_type->abi_align = abi_align;
struct_type->abi_size = next_offset;
struct_type->size_in_bits = next_offset * 8;
return struct_type;
}
static size_t get_store_size_bytes(size_t size_in_bits) {
return (size_in_bits + 7) / 8;
}
static size_t get_abi_align_bytes(size_t size_in_bits, size_t pointer_size_bytes) {
size_t store_size_bytes = get_store_size_bytes(size_in_bits);
if (store_size_bytes >= pointer_size_bytes)
return pointer_size_bytes;
return round_to_next_power_of_2(store_size_bytes);
}
static size_t get_abi_size_bytes(size_t size_in_bits, size_t pointer_size_bytes) {
size_t store_size_bytes = get_store_size_bytes(size_in_bits);
size_t abi_align = get_abi_align_bytes(size_in_bits, pointer_size_bytes);
return align_forward(store_size_bytes, abi_align);
}
static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
assert(struct_type->id == ZigTypeIdStruct);
Error err;
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
if (struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown)
return ErrorNone;
if ((err = resolve_struct_alignment(g, struct_type)))
return err;
AstNode *decl_node = struct_type->data.structure.decl_node;
if (struct_type->data.structure.resolve_loop_flag) {
if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
ErrorMsg *msg = add_node_error(g, decl_node,
buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
emit_error_notes_for_ref_stack(g, msg);
}
return ErrorSemanticAnalyzeFail;
}
assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
assert(decl_node->type == NodeTypeContainerDecl);
size_t field_count = struct_type->data.structure.src_field_count;
bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
struct_type->data.structure.resolve_loop_flag = true;
uint32_t *host_int_bytes = packed ? allocate<uint32_t>(struct_type->data.structure.gen_field_count) : nullptr;
// Resolve sizes of all the field types. Done before the offset loop because the offset
// loop has to look ahead.
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
}
size_t packed_bits_offset = 0;
size_t next_offset = 0;
size_t first_packed_bits_offset_misalign = SIZE_MAX;
size_t gen_field_index = 0;
size_t size_in_bits = 0;
size_t abi_align = struct_type->abi_align;
// Calculate offsets
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
if (field->gen_index == SIZE_MAX)
continue;
ZigType *field_type = field->type_entry;
assert(field_type != nullptr);
field->gen_index = gen_field_index;
field->offset = next_offset;
if (packed) {
size_t field_size_in_bits = type_size_bits(g, field_type);
size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
size_in_bits += field_size_in_bits;
if (first_packed_bits_offset_misalign != SIZE_MAX) {
// this field is not byte-aligned; it is part of the previous field with a bit offset
field->bit_offset_in_host = packed_bits_offset - first_packed_bits_offset_misalign;
size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign;
size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
if (full_abi_size * 8 == full_bit_count) {
// next field recovers ABI alignment
host_int_bytes[gen_field_index] = full_abi_size;
gen_field_index += 1;
// TODO: https://github.com/ziglang/zig/issues/1512
next_offset = next_field_offset(next_offset, abi_align, full_abi_size, 1);
size_in_bits = next_offset * 8;
first_packed_bits_offset_misalign = SIZE_MAX;
}
} else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) {
first_packed_bits_offset_misalign = packed_bits_offset;
field->bit_offset_in_host = 0;
} else {
// This is a byte-aligned field (both start and end) in a packed struct.
host_int_bytes[gen_field_index] = field_type->size_in_bits / 8;
field->bit_offset_in_host = 0;
gen_field_index += 1;
// TODO: https://github.com/ziglang/zig/issues/1512
next_offset = next_field_offset(next_offset, abi_align, field_type->size_in_bits / 8, 1);
size_in_bits = next_offset * 8;
}
packed_bits_offset = next_packed_bits_offset;
} else {
gen_field_index += 1;
size_t next_src_field_index = i + 1;
for (; next_src_field_index < field_count; next_src_field_index += 1) {
if (struct_type->data.structure.fields[next_src_field_index].gen_index != SIZE_MAX) {
break;
}
}
size_t next_abi_align = (next_src_field_index == field_count) ?
abi_align : struct_type->data.structure.fields[next_src_field_index].type_entry->abi_align;
next_offset = next_field_offset(next_offset, abi_align, field_type->abi_size, next_abi_align);
size_in_bits = next_offset * 8;
}
}
if (first_packed_bits_offset_misalign != SIZE_MAX) {
size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign;
size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
next_offset = next_field_offset(next_offset, abi_align, full_abi_size, abi_align);
host_int_bytes[gen_field_index] = full_abi_size;
gen_field_index += 1;
}
struct_type->abi_size = next_offset;
struct_type->size_in_bits = size_in_bits;
struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
struct_type->data.structure.resolve_loop_flag = false;
struct_type->data.structure.host_int_bytes = host_int_bytes;
return ErrorNone;
}
static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) {
assert(union_type->id == ZigTypeIdUnion);
Error err;
if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown)
return ErrorNone;
if ((err = resolve_union_zero_bits(g, union_type)))
return err;
if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown)
return ErrorNone;
if (union_type->data.unionation.resolve_loop_flag) {
if (!union_type->data.unionation.reported_infinite_err) {
AstNode *decl_node = union_type->data.unionation.decl_node;
union_type->data.unionation.reported_infinite_err = true;
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
ErrorMsg *msg = add_node_error(g, decl_node,
buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name)));
emit_error_notes_for_ref_stack(g, msg);
}
return ErrorSemanticAnalyzeFail;
}
// set temporary flag
union_type->data.unionation.resolve_loop_flag = true;
ZigType *most_aligned_union_member = nullptr;
uint32_t field_count = union_type->data.unionation.src_field_count;
bool packed = union_type->data.unionation.layout == ContainerLayoutPacked;
for (uint32_t i = 0; i < field_count; i += 1) {
TypeUnionField *field = &union_type->data.unionation.fields[i];
if (field->gen_index == UINT32_MAX)
continue;
size_t this_field_align;
if (packed) {
// TODO: https://github.com/ziglang/zig/issues/1512
this_field_align = 1;
// This is the same hack as resolve_struct_alignment. See the comment there.
} else if (field->type_entry == nullptr) {
this_field_align = g->builtin_types.entry_usize->abi_align;
} else {
if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
this_field_align = field->type_entry->abi_align;
}
if (most_aligned_union_member == nullptr ||
this_field_align > most_aligned_union_member->abi_align)
{
most_aligned_union_member = field->type_entry;
}
}
// unset temporary flag
union_type->data.unionation.resolve_loop_flag = false;
union_type->data.unionation.resolve_status = ResolveStatusAlignmentKnown;
union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
ZigType *tag_type = union_type->data.unionation.tag_type;
if (tag_type != nullptr && type_has_bits(tag_type)) {
if ((err = type_resolve(g, tag_type, ResolveStatusAlignmentKnown))) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (most_aligned_union_member == nullptr) {
union_type->abi_align = tag_type->abi_align;
union_type->data.unionation.gen_tag_index = SIZE_MAX;
union_type->data.unionation.gen_union_index = SIZE_MAX;
} else if (tag_type->abi_align > most_aligned_union_member->abi_align) {
union_type->abi_align = tag_type->abi_align;
union_type->data.unionation.gen_tag_index = 0;
union_type->data.unionation.gen_union_index = 1;
} else {
union_type->abi_align = most_aligned_union_member->abi_align;
union_type->data.unionation.gen_union_index = 0;
union_type->data.unionation.gen_tag_index = 1;
}
} else {
assert(most_aligned_union_member != nullptr);
union_type->abi_align = most_aligned_union_member->abi_align;
union_type->data.unionation.gen_union_index = SIZE_MAX;
union_type->data.unionation.gen_tag_index = SIZE_MAX;
}
return ErrorNone;
}
static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
assert(union_type->id == ZigTypeIdUnion);
Error err;
if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
if (union_type->data.unionation.resolve_status >= ResolveStatusSizeKnown)
return ErrorNone;
if ((err = resolve_union_alignment(g, union_type)))
return err;
AstNode *decl_node = union_type->data.unionation.decl_node;
assert(decl_node->type == NodeTypeContainerDecl);
uint32_t field_count = union_type->data.unionation.src_field_count;
ZigType *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;
assert(union_type->data.unionation.fields);
size_t union_abi_size = 0;
size_t union_size_in_bits = 0;
if (union_type->data.unionation.resolve_loop_flag) {
if (!union_type->data.unionation.reported_infinite_err) {
union_type->data.unionation.reported_infinite_err = true;
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
ErrorMsg *msg = add_node_error(g, decl_node,
buf_sprintf("union '%s' depends on its own size", buf_ptr(&union_type->name)));
emit_error_notes_for_ref_stack(g, msg);
}
return ErrorSemanticAnalyzeFail;
}
// set temporary flag
union_type->data.unionation.resolve_loop_flag = true;
for (uint32_t i = 0; i < field_count; i += 1) {
TypeUnionField *union_field = &union_type->data.unionation.fields[i];
ZigType *field_type = union_field->type_entry;
if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (type_is_invalid(union_type))
return ErrorSemanticAnalyzeFail;
if (!type_has_bits(field_type))
continue;
union_abi_size = max(union_abi_size, field_type->abi_size);
union_size_in_bits = max(union_size_in_bits, field_type->size_in_bits);
}
// The union itself for now has to be treated as being independently aligned.
// See https://github.com/ziglang/zig/issues/2166.
if (most_aligned_union_member != nullptr) {
union_abi_size = align_forward(union_abi_size, most_aligned_union_member->abi_align);
}
// unset temporary flag
union_type->data.unionation.resolve_loop_flag = false;
union_type->data.unionation.resolve_status = ResolveStatusSizeKnown;
union_type->data.unionation.union_abi_size = union_abi_size;
ZigType *tag_type = union_type->data.unionation.tag_type;
if (tag_type != nullptr && type_has_bits(tag_type)) {
if ((err = type_resolve(g, tag_type, ResolveStatusSizeKnown))) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (most_aligned_union_member == nullptr) {
union_type->abi_size = tag_type->abi_size;
union_type->size_in_bits = tag_type->size_in_bits;
} else {
size_t field_sizes[2];
size_t field_aligns[2];
field_sizes[union_type->data.unionation.gen_tag_index] = tag_type->abi_size;
field_aligns[union_type->data.unionation.gen_tag_index] = tag_type->abi_align;
field_sizes[union_type->data.unionation.gen_union_index] = union_abi_size;
field_aligns[union_type->data.unionation.gen_union_index] = most_aligned_union_member->abi_align;
size_t field2_offset = next_field_offset(0, union_type->abi_align, field_sizes[0], field_aligns[1]);
union_type->abi_size = next_field_offset(field2_offset, union_type->abi_align, field_sizes[1], union_type->abi_align);
union_type->size_in_bits = union_type->abi_size * 8;
}
} else {
union_type->abi_size = union_abi_size;
union_type->size_in_bits = union_size_in_bits;
}
return ErrorNone;
}
static bool type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty) {
// Only integer types are allowed by the C ABI
if(ty->id != ZigTypeIdInt)
return false;
// According to the ANSI C standard the enumeration type should be either a
// signed char, a signed integer or an unsigned one. But GCC/Clang allow
// other integral types as a compiler extension so let's accomodate them
// aswell.
return type_allowed_in_extern(g, ty);
}
static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
assert(enum_type->id == ZigTypeIdEnum);
if (enum_type->data.enumeration.is_invalid)
return ErrorSemanticAnalyzeFail;
if (enum_type->data.enumeration.zero_bits_known)
return ErrorNone;
if (enum_type->data.enumeration.zero_bits_loop_flag) {
ErrorMsg *msg = add_node_error(g, enum_type->data.enumeration.decl_node,
buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name)));
emit_error_notes_for_ref_stack(g, msg);
enum_type->data.enumeration.is_invalid = true;
return ErrorSemanticAnalyzeFail;
}
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->data.enumeration.fields);
uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
if (field_count == 0) {
add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
enum_type->data.enumeration.src_field_count = field_count;
enum_type->data.enumeration.fields = nullptr;
enum_type->data.enumeration.is_invalid = true;
enum_type->data.enumeration.zero_bits_loop_flag = false;
enum_type->data.enumeration.zero_bits_known = true;
return ErrorSemanticAnalyzeFail;
}
enum_type->data.enumeration.src_field_count = field_count;
enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
enum_type->data.enumeration.fields_by_name.init(field_count);
Scope *scope = &enum_type->data.enumeration.decls_scope->base;
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
occupied_tag_values.init(field_count);
ZigType *tag_int_type;
if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
tag_int_type = get_c_int_type(g, CIntTypeInt);
} else if (enum_type->data.enumeration.layout == ContainerLayoutAuto && field_count == 1) {
tag_int_type = g->builtin_types.entry_num_lit_int;
} else {
tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
}
enum_type->size_in_bits = tag_int_type->size_in_bits;
enum_type->abi_size = tag_int_type->abi_size;
enum_type->abi_align = tag_int_type->abi_align;
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
if (type_is_invalid(wanted_tag_int_type)) {
enum_type->data.enumeration.is_invalid = true;
} else if (wanted_tag_int_type->id != ZigTypeIdInt) {
enum_type->data.enumeration.is_invalid = true;
add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
} else if (enum_type->data.enumeration.layout == ContainerLayoutExtern &&
!type_is_valid_extern_enum_tag(g, wanted_tag_int_type)) {
enum_type->data.enumeration.is_invalid = true;
ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("'%s' is not a valid tag type for an extern enum",
buf_ptr(&wanted_tag_int_type->name)));
add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
} else {
tag_int_type = wanted_tag_int_type;
}
}
enum_type->data.enumeration.tag_int_type = tag_int_type;
enum_type->size_in_bits = tag_int_type->size_in_bits;
enum_type->abi_size = tag_int_type->abi_size;
enum_type->abi_align = tag_int_type->abi_align;
BigInt bi_one;
bigint_init_unsigned(&bi_one, 1);
TypeEnumField *last_enum_field = nullptr;
for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
type_enum_field->name = field_node->data.struct_field.name;
type_enum_field->decl_index = field_i;
type_enum_field->decl_node = field_node;
if (field_node->data.struct_field.type != nullptr) {
ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,
buf_sprintf("structs and unions, not enums, support field types"));
add_error_note(g, msg, decl_node,
buf_sprintf("consider 'union(enum)' here"));
}
auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
if (field_entry != nullptr) {
ErrorMsg *msg = add_node_error(g, field_node,
buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));
add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
enum_type->data.enumeration.is_invalid = true;
continue;
}
AstNode *tag_value = field_node->data.struct_field.value;
if (tag_value != nullptr) {
// A user-specified value is available
ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
if (type_is_invalid(result->type)) {
enum_type->data.enumeration.is_invalid = true;
continue;
}
assert(result->special != ConstValSpecialRuntime);
assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
} else {
// No value was explicitly specified: allocate the last value + 1
// or, if this is the first element, zero
if (last_enum_field != nullptr) {
bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);
} else {
bigint_init_unsigned(&type_enum_field->value, 0);
}
// Make sure we can represent this number with tag_int_type
if (!bigint_fits_in_bits(&type_enum_field->value,
tag_int_type->size_in_bits,
tag_int_type->data.integral.is_signed)) {
enum_type->data.enumeration.is_invalid = true;
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &type_enum_field->value, 10);
add_node_error(g, field_node,
buf_sprintf("enumeration value %s too large for type '%s'",
buf_ptr(val_buf), buf_ptr(&tag_int_type->name)));
break;
}
}
// Make sure the value is unique
auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
if (entry != nullptr) {
enum_type->data.enumeration.is_invalid = true;
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &type_enum_field->value, 10);
ErrorMsg *msg = add_node_error(g, field_node,
buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
add_error_note(g, msg, entry->value,
buf_sprintf("other occurrence here"));
}
last_enum_field = type_enum_field;
}
enum_type->data.enumeration.zero_bits_loop_flag = false;
enum_type->data.enumeration.zero_bits_known = true;
enum_type->data.enumeration.complete = true;
if (enum_type->data.enumeration.is_invalid)
return ErrorSemanticAnalyzeFail;
return ErrorNone;
}
static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
assert(struct_type->id == ZigTypeIdStruct);
Error err;
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
if (struct_type->data.structure.resolve_status >= ResolveStatusZeroBitsKnown)
return ErrorNone;
AstNode *decl_node = struct_type->data.structure.decl_node;
assert(decl_node->type == NodeTypeContainerDecl);
if (struct_type->data.structure.resolve_loop_flag) {
// TODO This is a problem. I believe it can be solved with lazy values.
struct_type->size_in_bits = SIZE_MAX;
struct_type->abi_size = SIZE_MAX;
struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown;
struct_type->data.structure.resolve_loop_flag = false;
return ErrorNone;
}
struct_type->data.structure.resolve_loop_flag = true;
assert(!struct_type->data.structure.fields);
size_t field_count = decl_node->data.container_decl.fields.length;
struct_type->data.structure.src_field_count = (uint32_t)field_count;
struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
struct_type->data.structure.fields_by_name.init(field_count);
Scope *scope = &struct_type->data.structure.decls_scope->base;
size_t gen_field_index = 0;
for (size_t i = 0; i < field_count; i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
type_struct_field->name = field_node->data.struct_field.name;
type_struct_field->decl_node = field_node;
if (field_node->data.struct_field.type == nullptr) {
add_node_error(g, field_node, buf_sprintf("struct field missing type"));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field);
if (field_entry != nullptr) {
ErrorMsg *msg = add_node_error(g, field_node,
buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name)));
add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
ZigType *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
type_struct_field->type_entry = field_type;
if (type_is_invalid(field_type)) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
if (struct_type->data.structure.layout == ContainerLayoutExtern &&
!type_allowed_in_extern(g, field_type))
{
add_node_error(g, field_node,
buf_sprintf("extern structs cannot contain fields of type '%s'",
buf_ptr(&field_type->name)));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
} else if (struct_type->data.structure.layout == ContainerLayoutPacked) {
if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field_type, field_node))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
}
type_struct_field->src_index = i;
type_struct_field->gen_index = SIZE_MAX;
if (field_node->data.struct_field.value != nullptr) {
add_node_error(g, field_node->data.struct_field.value,
buf_sprintf("enums, not structs, support field assignment"));
}
if (field_type->id == ZigTypeIdOpaque) {
add_node_error(g, field_node->data.struct_field.type,
buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs"));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
switch (type_requires_comptime(g, field_type)) {
case ReqCompTimeYes:
struct_type->data.structure.requires_comptime = true;
break;
case ReqCompTimeInvalid:
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
case ReqCompTimeNo:
break;
}
if (!type_has_bits(field_type))
continue;
type_struct_field->gen_index = gen_field_index;
gen_field_index += 1;
}
struct_type->data.structure.resolve_loop_flag = false;
struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
if (gen_field_index != 0) {
struct_type->abi_size = SIZE_MAX;
struct_type->size_in_bits = SIZE_MAX;
}
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown;
return ErrorNone;
}
static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
assert(struct_type->id == ZigTypeIdStruct);
Error err;
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown)
return ErrorNone;
if ((err = resolve_struct_zero_bits(g, struct_type)))
return err;
if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown)
return ErrorNone;
AstNode *decl_node = struct_type->data.structure.decl_node;
if (struct_type->data.structure.resolve_loop_flag) {
if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
ErrorMsg *msg = add_node_error(g, decl_node,
buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
emit_error_notes_for_ref_stack(g, msg);
}
return ErrorSemanticAnalyzeFail;
}
struct_type->data.structure.resolve_loop_flag = true;
assert(decl_node->type == NodeTypeContainerDecl);
size_t field_count = struct_type->data.structure.src_field_count;
bool packed = struct_type->data.structure.layout == ContainerLayoutPacked;
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
if (field->gen_index == SIZE_MAX)
continue;
size_t this_field_align;
if (packed) {
// TODO: https://github.com/ziglang/zig/issues/1512
this_field_align = 1;
// TODO If we have no type_entry for the field, we've already failed to
// compile the program correctly. This stage1 compiler needs a deeper
// reworking to make this correct, or we can ignore the problem
// and make sure it is fixed in stage2. This workaround is for when
// there is a false positive of a dependency loop, of alignment depending
// on itself. When this false positive happens we assume a pointer-aligned
// field, which is usually fine but could be incorrectly over-aligned or
// even under-aligned. See https://github.com/ziglang/zig/issues/1512
} else if (field->type_entry == nullptr) {
this_field_align = g->builtin_types.entry_usize->abi_align;
} else {
if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
this_field_align = field->type_entry->abi_align;
}
// TODO: https://github.com/ziglang/zig/issues/1512
if (this_field_align > struct_type->abi_align) {
struct_type->abi_align = this_field_align;
}
}
struct_type->data.structure.resolve_loop_flag = false;
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) {
return ErrorSemanticAnalyzeFail;
}
struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown;
return ErrorNone;
}
static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
assert(union_type->id == ZigTypeIdUnion);
Error err;
if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
if (union_type->data.unionation.resolve_status >= ResolveStatusZeroBitsKnown)
return ErrorNone;
if (union_type->data.unionation.resolve_loop_flag) {
// If we get here it's due to recursion. From this we conclude that the struct is
// not zero bits.
// TODO actually it could still be zero bits. Here we should continue analyzing
// the union from the next field index.
union_type->data.unionation.resolve_status = ResolveStatusZeroBitsKnown;
union_type->data.unionation.resolve_loop_flag = false;
union_type->abi_size = SIZE_MAX;
union_type->size_in_bits = SIZE_MAX;
return ErrorNone;
}
union_type->data.unionation.resolve_loop_flag = true;
AstNode *decl_node = union_type->data.unionation.decl_node;
assert(decl_node->type == NodeTypeContainerDecl);
assert(union_type->data.unionation.fields == nullptr);
uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
if (field_count == 0) {
add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
union_type->data.unionation.src_field_count = field_count;
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
union_type->data.unionation.src_field_count = field_count;
union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
union_type->data.unionation.fields_by_name.init(field_count);
Scope *scope = &union_type->data.unionation.decls_scope->base;
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr;
union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum ||
enum_type_node != nullptr;
bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr) && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);
ZigType *tag_type;
bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety);
bool *covered_enum_fields;
ZigLLVMDIEnumerator **di_enumerators;
if (create_enum_type) {
occupied_tag_values.init(field_count);
di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
ZigType *tag_int_type;
if (enum_type_node != nullptr) {
tag_int_type = analyze_type_expr(g, scope, enum_type_node);
if (type_is_invalid(tag_int_type)) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (tag_int_type->id != ZigTypeIdInt) {
add_node_error(g, enum_type_node,
buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name)));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
} else if (auto_layout && field_count == 1) {
tag_int_type = g->builtin_types.entry_num_lit_int;
} else {
tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
}
tag_type = new_type_table_entry(ZigTypeIdEnum);
buf_resize(&tag_type->name, 0);
buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name));
tag_type->llvm_type = tag_int_type->llvm_type;
tag_type->llvm_di_type = tag_int_type->llvm_di_type;
tag_type->abi_size = tag_int_type->abi_size;
tag_type->abi_align = tag_int_type->abi_align;
tag_type->size_in_bits = tag_int_type->size_in_bits;
tag_type->data.enumeration.tag_int_type = tag_int_type;
tag_type->data.enumeration.zero_bits_known = true;
tag_type->data.enumeration.decl_node = decl_node;
tag_type->data.enumeration.layout = ContainerLayoutAuto;
tag_type->data.enumeration.src_field_count = field_count;
tag_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
tag_type->data.enumeration.fields_by_name.init(field_count);
tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;
tag_type->data.enumeration.complete = true;
} else if (enum_type_node != nullptr) {
ZigType *enum_type = analyze_type_expr(g, scope, enum_type_node);
if (type_is_invalid(enum_type)) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (enum_type->id != ZigTypeIdEnum) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
add_node_error(g, enum_type_node,
buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
return ErrorSemanticAnalyzeFail;
}
if ((err = type_resolve(g, enum_type, ResolveStatusAlignmentKnown))) {
assert(g->errors.length != 0);
return err;
}
tag_type = enum_type;
covered_enum_fields = allocate<bool>(enum_type->data.enumeration.src_field_count);
} else {
tag_type = nullptr;
}
union_type->data.unionation.tag_type = tag_type;
uint32_t gen_field_index = 0;
for (uint32_t i = 0; i < field_count; i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
Buf *field_name = field_node->data.struct_field.name;
TypeUnionField *union_field = &union_type->data.unionation.fields[i];
union_field->name = field_node->data.struct_field.name;
union_field->decl_node = field_node;
union_field->gen_index = UINT32_MAX;
auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
if (field_entry != nullptr) {
ErrorMsg *msg = add_node_error(g, field_node,
buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
ZigType *field_type;
if (field_node->data.struct_field.type == nullptr) {
if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) {
field_type = g->builtin_types.entry_void;
} else {
add_node_error(g, field_node, buf_sprintf("union field missing type"));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
} else {
field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
if ((err = type_resolve(g, field_type, ResolveStatusAlignmentKnown))) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
}
union_field->type_entry = field_type;
if (field_type->id == ZigTypeIdOpaque) {
add_node_error(g, field_node->data.struct_field.type,
buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in unions"));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
switch (type_requires_comptime(g, field_type)) {
case ReqCompTimeInvalid:
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
case ReqCompTimeYes:
union_type->data.unionation.requires_comptime = true;
break;
case ReqCompTimeNo:
break;
}
if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {
ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
buf_sprintf("non-enum union field assignment"));
add_error_note(g, msg, decl_node,
buf_sprintf("consider 'union(enum)' here"));
}
if (create_enum_type) {
di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i);
union_field->enum_field = &tag_type->data.enumeration.fields[i];
union_field->enum_field->name = field_name;
union_field->enum_field->decl_index = i;
union_field->enum_field->decl_node = field_node;
auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);
assert(prev_entry == nullptr); // caught by union de-duplicator above
AstNode *tag_value = field_node->data.struct_field.value;
// In this first pass we resolve explicit tag values.
// In a second pass we will fill in the unspecified ones.
if (tag_value != nullptr) {
ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type;
ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
if (type_is_invalid(result->type)) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
assert(result->special != ConstValSpecialRuntime);
assert(result->type->id == ZigTypeIdInt);
auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value);
if (entry == nullptr) {
bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint);
} else {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &result->data.x_bigint, 10);
ErrorMsg *msg = add_node_error(g, tag_value,
buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
add_error_note(g, msg, entry->value,
buf_sprintf("other occurrence here"));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
}
} else if (enum_type_node != nullptr) {
union_field->enum_field = find_enum_type_field(tag_type, field_name);
if (union_field->enum_field == nullptr) {
ErrorMsg *msg = add_node_error(g, field_node,
buf_sprintf("enum field not found: '%s'", buf_ptr(field_name)));
add_error_note(g, msg, tag_type->data.enumeration.decl_node,
buf_sprintf("enum declared here"));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
covered_enum_fields[union_field->enum_field->decl_index] = true;
} else {
union_field->enum_field = allocate<TypeEnumField>(1);
union_field->enum_field->name = field_name;
union_field->enum_field->decl_index = i;
bigint_init_unsigned(&union_field->enum_field->value, i);
}
assert(union_field->enum_field != nullptr);
if (!type_has_bits(field_type))
continue;
union_field->gen_index = gen_field_index;
gen_field_index += 1;
}
bool src_have_tag = decl_node->data.container_decl.auto_enum ||
decl_node->data.container_decl.init_arg_expr != nullptr;
if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) {
const char *qual_str;
switch (union_type->data.unionation.layout) {
case ContainerLayoutAuto:
zig_unreachable();
case ContainerLayoutPacked:
qual_str = "packed";
break;
case ContainerLayoutExtern:
qual_str = "extern";
break;
}
AstNode *source_node = (decl_node->data.container_decl.init_arg_expr != nullptr) ?
decl_node->data.container_decl.init_arg_expr : decl_node;
add_node_error(g, source_node,
buf_sprintf("%s union does not support enum tag type", qual_str));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
if (create_enum_type) {
// Now iterate again and populate the unspecified tag values
uint32_t next_maybe_unoccupied_index = 0;
for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];
AstNode *tag_value = field_node->data.struct_field.value;
if (tag_value == nullptr) {
if (occupied_tag_values.size() == 0) {
bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index);
next_maybe_unoccupied_index += 1;
} else {
BigInt proposed_value;
for (;;) {
bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
next_maybe_unoccupied_index += 1;
auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
if (entry != nullptr) {
continue;
}
break;
}
bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
}
}
}
} else if (enum_type_node != nullptr) {
for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
if (!covered_enum_fields[i]) {
AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
ErrorMsg *msg = add_node_error(g, decl_node,
buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name)));
add_error_note(g, msg, field_node,
buf_sprintf("declared here"));
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
}
}
}
if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) {
return ErrorSemanticAnalyzeFail;
}
union_type->data.unionation.resolve_loop_flag = false;
union_type->data.unionation.gen_field_count = gen_field_index;
bool zero_bits = gen_field_index == 0 && (field_count < 2 || !src_have_tag);
if (!zero_bits) {
union_type->abi_size = SIZE_MAX;
union_type->size_in_bits = SIZE_MAX;
}
union_type->data.unionation.resolve_status = zero_bits ? ResolveStatusSizeKnown : ResolveStatusZeroBitsKnown;
return ErrorNone;
}
static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, bool is_test) {
buf_resize(buf, 0);
Scope *scope = tld->parent_scope;
while (scope->id != ScopeIdDecls) {
scope = scope->parent;
}
ScopeDecls *decls_scope = reinterpret_cast<ScopeDecls *>(scope);
buf_append_buf(buf, &decls_scope->container_type->name);
if (buf_len(buf) != 0) buf_append_char(buf, NAMESPACE_SEP_CHAR);
if (is_test) {
buf_append_str(buf, "test \"");
buf_append_buf(buf, tld->name);
buf_append_char(buf, '"');
} else {
buf_append_buf(buf, tld->name);
}
}
ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
ZigFn *fn_entry = allocate<ZigFn>(1);
fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota;
fn_entry->codegen = g;
fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota;
fn_entry->analyzed_executable.fn_entry = fn_entry;
fn_entry->ir_executable.fn_entry = fn_entry;
fn_entry->fn_inline = inline_value;
return fn_entry;
}
ZigFn *create_fn(CodeGen *g, AstNode *proto_node) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
FnInline inline_value = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto;
ZigFn *fn_entry = create_fn_raw(g, inline_value);
fn_entry->proto_node = proto_node;
fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
proto_node->data.fn_proto.fn_def_node->data.fn_def.body;
return fn_entry;
}
static bool scope_is_root_decls(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdDecls) {
ScopeDecls *scope_decls = (ScopeDecls *)scope;
return is_top_level_struct(scope_decls->container_type);
}
scope = scope->parent;
}
zig_unreachable();
}
void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn) {
ConstExprValue *panic_fn_type_val = get_builtin_value(g, "PanicFn");
assert(panic_fn_type_val != nullptr);
assert(panic_fn_type_val->type->id == ZigTypeIdMetaType);
ZigType *panic_fn_type = panic_fn_type_val->data.x_type;
AstNode *fake_decl = allocate<AstNode>(1);
*fake_decl = *panic_fn->proto_node;
fake_decl->type = NodeTypeSymbol;
fake_decl->data.symbol_expr.symbol = tld_fn->base.name;
// call this for the side effects of casting to panic_fn_type
analyze_const_value(g, tld_fn->base.parent_scope, fake_decl, panic_fn_type, nullptr);
}
ZigType *get_test_fn_type(CodeGen *g) {
if (g->test_fn_type)
return g->test_fn_type;
FnTypeId fn_type_id = {0};
fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set,
g->builtin_types.entry_void);
g->test_fn_type = get_fn_type(g, &fn_type_id);
return g->test_fn_type;
}
void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc) {
if (ccc) {
if (buf_eql_str(symbol_name, "main") && g->libc_link_lib != nullptr) {
g->have_c_main = true;
g->subsystem = TargetSubsystemConsole;
} else if (buf_eql_str(symbol_name, "WinMain") &&
g->zig_target->os == OsWindows)
{
g->have_winmain = true;
g->subsystem = TargetSubsystemWindows;
} else if (buf_eql_str(symbol_name, "WinMainCRTStartup") &&
g->zig_target->os == OsWindows)
{
g->have_winmain_crt_startup = true;
} else if (buf_eql_str(symbol_name, "DllMainCRTStartup") &&
g->zig_target->os == OsWindows)
{
g->have_dllmain_crt_startup = true;
}
}
FnExport *fn_export = fn_table_entry->export_list.add_one();
memset(fn_export, 0, sizeof(FnExport));
buf_init_from_buf(&fn_export->name, symbol_name);
fn_export->linkage = linkage;
}
static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
ZigType *import = tld_fn->base.import;
AstNode *source_node = tld_fn->base.source_node;
if (source_node->type == NodeTypeFnProto) {
AstNodeFnProto *fn_proto = &source_node->data.fn_proto;
AstNode *fn_def_node = fn_proto->fn_def_node;
ZigFn *fn_table_entry = create_fn(g, source_node);
tld_fn->fn_entry = fn_table_entry;
bool is_extern = (fn_table_entry->body_node == nullptr);
if (fn_proto->is_export || is_extern) {
buf_init_from_buf(&fn_table_entry->symbol_name, tld_fn->base.name);
} else {
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, false);
}
if (fn_proto->is_export) {
bool ccc = (fn_proto->cc == CallingConventionUnspecified || fn_proto->cc == CallingConventionC);
add_fn_export(g, fn_table_entry, &fn_table_entry->symbol_name, GlobalLinkageIdStrong, ccc);
}
if (!is_extern) {
fn_table_entry->fndef_scope = create_fndef_scope(g,
fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry);
for (size_t i = 0; i < fn_proto->params.length; i += 1) {
AstNode *param_node = fn_proto->params.at(i);
assert(param_node->type == NodeTypeParamDecl);
if (param_node->data.param_decl.name == nullptr) {
add_node_error(g, param_node, buf_sprintf("missing parameter name"));
}
}
} else {
g->external_prototypes.put_unique(tld_fn->base.name, &tld_fn->base);
}
Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry);
if (fn_proto->section_expr != nullptr) {
if (fn_table_entry->body_node == nullptr) {
add_node_error(g, fn_proto->section_expr,
buf_sprintf("cannot set section of external function '%s'", buf_ptr(&fn_table_entry->symbol_name)));
} else {
analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name);
}
}
if (fn_table_entry->type_entry->id == ZigTypeIdInvalid) {
tld_fn->base.resolution = TldResolutionInvalid;
return;
}
if (!fn_table_entry->type_entry->data.fn.is_generic) {
if (fn_def_node)
g->fn_defs.append(fn_table_entry);
}
if (scope_is_root_decls(tld_fn->base.parent_scope) &&
(import == g->root_import || import->data.structure.root_struct->package == g->panic_package))
{
if (g->have_pub_main && buf_eql_str(tld_fn->base.name, "main")) {
g->main_fn = fn_table_entry;
} else if ((import->data.structure.root_struct->package == g->panic_package || g->have_pub_panic) &&
buf_eql_str(tld_fn->base.name, "panic"))
{
g->panic_fn = fn_table_entry;
g->panic_tld_fn = tld_fn;
}
}
} else if (source_node->type == NodeTypeTestDecl) {
ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto);
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, true);
tld_fn->fn_entry = fn_table_entry;
fn_table_entry->proto_node = source_node;
fn_table_entry->fndef_scope = create_fndef_scope(g, source_node, tld_fn->base.parent_scope, fn_table_entry);
fn_table_entry->type_entry = get_test_fn_type(g);
fn_table_entry->body_node = source_node->data.test_decl.body;
fn_table_entry->is_test = true;
g->fn_defs.append(fn_table_entry);
g->test_fns.append(fn_table_entry);
} else {
zig_unreachable();
}
}
static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) {
assert(tld_comptime->base.source_node->type == NodeTypeCompTime);
AstNode *expr_node = tld_comptime->base.source_node->data.comptime_expr.expr;
analyze_const_value(g, tld_comptime->base.parent_scope, expr_node, g->builtin_types.entry_void, nullptr);
}
static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
bool is_export = false;
if (tld->id == TldIdVar) {
assert(tld->source_node->type == NodeTypeVariableDeclaration);
is_export = tld->source_node->data.variable_declaration.is_export;
} else if (tld->id == TldIdFn) {
assert(tld->source_node->type == NodeTypeFnProto);
is_export = tld->source_node->data.fn_proto.is_export;
if (!is_export && !tld->source_node->data.fn_proto.is_extern &&
tld->source_node->data.fn_proto.fn_def_node == nullptr)
{
add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body"));
return;
}
}
if (is_export) {
g->resolve_queue.append(tld);
auto entry = g->exported_symbol_names.put_unique(tld->name, tld);
if (entry) {
AstNode *other_source_node = entry->value->source_node;
ErrorMsg *msg = add_node_error(g, tld->source_node,
buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name)));
add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here"));
}
}
{
auto entry = decls_scope->decl_table.put_unique(tld->name, tld);
if (entry) {
Tld *other_tld = entry->value;
ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("redefinition of '%s'", buf_ptr(tld->name)));
add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition is here"));
return;
}
}
{
ZigType *type;
if (get_primitive_type(g, tld->name, &type) != ErrorPrimitiveTypeNotFound) {
add_node_error(g, tld->source_node,
buf_sprintf("declaration shadows primitive type '%s'", buf_ptr(tld->name)));
}
}
}
static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
assert(node->type == NodeTypeTestDecl);
if (!g->is_test_build)
return;
ZigType *import = get_scope_import(&decls_scope->base);
if (import->data.structure.root_struct->package != g->root_package)
return;
Buf *decl_name_buf = node->data.test_decl.name;
Buf *test_name = g->test_name_prefix ?
buf_sprintf("%s%s", buf_ptr(g->test_name_prefix), buf_ptr(decl_name_buf)) : decl_name_buf;
if (g->test_filter != nullptr && strstr(buf_ptr(test_name), buf_ptr(g->test_filter)) == nullptr) {
return;
}
TldFn *tld_fn = allocate<TldFn>(1);
init_tld(&tld_fn->base, TldIdFn, test_name, VisibModPrivate, node, &decls_scope->base);
g->resolve_queue.append(&tld_fn->base);
}
static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
assert(node->type == NodeTypeCompTime);
TldCompTime *tld_comptime = allocate<TldCompTime>(1);
init_tld(&tld_comptime->base, TldIdCompTime, nullptr, VisibModPrivate, node, &decls_scope->base);
g->resolve_queue.append(&tld_comptime->base);
}
void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node,
Scope *parent_scope)
{
tld->id = id;
tld->name = name;
tld->visib_mod = visib_mod;
tld->source_node = source_node;
tld->import = source_node ? source_node->owner : nullptr;
tld->parent_scope = parent_scope;
}
void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) {
Tld *tld = get_container_scope(g->compile_var_import)->decl_table.get(name);
resolve_top_level_decl(g, tld, tld->source_node);
assert(tld->id == TldIdVar);
TldVar *tld_var = (TldVar *)tld;
tld_var->var->const_value = value;
tld_var->var->var_type = value->type;
tld_var->var->align_bytes = get_abi_alignment(g, value->type);
}
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
switch (node->type) {
case NodeTypeContainerDecl:
for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) {
AstNode *child = node->data.container_decl.decls.at(i);
scan_decls(g, decls_scope, child);
}
break;
case NodeTypeFnDef:
scan_decls(g, decls_scope, node->data.fn_def.fn_proto);
break;
case NodeTypeVariableDeclaration:
{
Buf *name = node->data.variable_declaration.symbol;
VisibMod visib_mod = node->data.variable_declaration.visib_mod;
TldVar *tld_var = allocate<TldVar>(1);
init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base);
tld_var->extern_lib_name = node->data.variable_declaration.lib_name;
add_top_level_decl(g, decls_scope, &tld_var->base);
break;
}
case NodeTypeFnProto:
{
// if the name is missing, we immediately announce an error
Buf *fn_name = node->data.fn_proto.name;
if (fn_name == nullptr) {
add_node_error(g, node, buf_sprintf("missing function name"));
break;
}
VisibMod visib_mod = node->data.fn_proto.visib_mod;
TldFn *tld_fn = allocate<TldFn>(1);
init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base);
tld_fn->extern_lib_name = node->data.fn_proto.lib_name;
add_top_level_decl(g, decls_scope, &tld_fn->base);
break;
}
case NodeTypeUse:
{
g->use_queue.append(node);
decls_scope->use_decls.append(node);
break;
}
case NodeTypeTestDecl:
preview_test_decl(g, node, decls_scope);
break;
case NodeTypeCompTime:
preview_comptime_decl(g, node, decls_scope);
break;
case NodeTypeParamDecl:
case NodeTypeReturnExpr:
case NodeTypeDefer:
case NodeTypeBlock:
case NodeTypeGroupedExpr:
case NodeTypeBinOpExpr:
case NodeTypeUnwrapErrorExpr:
case NodeTypeFnCallExpr:
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeFloatLiteral:
case NodeTypeIntLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeUndefinedLiteral:
case NodeTypeSymbol:
case NodeTypePrefixOpExpr:
case NodeTypePointerType:
case NodeTypeIfBoolExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeUnreachable:
case NodeTypeAsmExpr:
case NodeTypeFieldAccessExpr:
case NodeTypePtrDeref:
case NodeTypeUnwrapOptional:
case NodeTypeStructField:
case NodeTypeContainerInitExpr:
case NodeTypeStructValueField:
case NodeTypeArrayType:
case NodeTypeErrorType:
case NodeTypeIfErrorExpr:
case NodeTypeIfOptional:
case NodeTypeErrorSetDecl:
case NodeTypeCancel:
case NodeTypeResume:
case NodeTypeAwaitExpr:
case NodeTypeSuspend:
case NodeTypePromiseType:
case NodeTypeEnumLiteral:
zig_unreachable();
}
}
static Error resolve_decl_container(CodeGen *g, TldContainer *tld_container) {
ZigType *type_entry = tld_container->type_entry;
assert(type_entry);
switch (type_entry->id) {
case ZigTypeIdStruct:
return resolve_struct_type(g, tld_container->type_entry);
case ZigTypeIdEnum:
return resolve_enum_zero_bits(g, tld_container->type_entry);
case ZigTypeIdUnion:
return resolve_union_type(g, tld_container->type_entry);
default:
zig_unreachable();
}
}
ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
return g->builtin_types.entry_invalid;
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed",
buf_ptr(&type_entry->name)));
return g->builtin_types.entry_invalid;
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
case ZigTypeIdVector:
return type_entry;
}
zig_unreachable();
}
// Set name to nullptr to make the variable anonymous (not visible to programmer).
// TODO merge with definition of add_local_var in ir.cpp
ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
bool is_const, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type)
{
Error err;
assert(const_value != nullptr);
assert(var_type != nullptr);
ZigVar *variable_entry = allocate<ZigVar>(1);
variable_entry->const_value = const_value;
variable_entry->var_type = var_type;
variable_entry->parent_scope = parent_scope;
variable_entry->shadowable = false;
variable_entry->mem_slot_index = SIZE_MAX;
variable_entry->src_arg_index = SIZE_MAX;
assert(name);
buf_init_from_buf(&variable_entry->name, name);
if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) {
variable_entry->var_type = g->builtin_types.entry_invalid;
} else {
variable_entry->align_bytes = get_abi_alignment(g, var_type);
ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr);
if (existing_var && !existing_var->shadowable) {
if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
ErrorMsg *msg = add_node_error(g, source_node,
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
}
variable_entry->var_type = g->builtin_types.entry_invalid;
} else {
ZigType *type;
if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) {
add_node_error(g, source_node,
buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
variable_entry->var_type = g->builtin_types.entry_invalid;
} else {
Scope *search_scope = nullptr;
if (src_tld == nullptr) {
search_scope = parent_scope;
} else if (src_tld->parent_scope != nullptr && src_tld->parent_scope->parent != nullptr) {
search_scope = src_tld->parent_scope->parent;
}
if (search_scope != nullptr) {
Tld *tld = find_decl(g, search_scope, name);
if (tld != nullptr) {
ErrorMsg *msg = add_node_error(g, source_node,
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here"));
variable_entry->var_type = g->builtin_types.entry_invalid;
}
}
}
}
}
Scope *child_scope;
if (source_node && source_node->type == NodeTypeParamDecl) {
child_scope = create_var_scope(g, source_node, parent_scope, variable_entry);
} else {
// it's already in the decls table
child_scope = parent_scope;
}
variable_entry->src_is_const = is_const;
variable_entry->gen_is_const = is_const;
variable_entry->decl_node = source_node;
variable_entry->child_scope = child_scope;
return variable_entry;
}
static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
AstNode *source_node = tld_var->base.source_node;
AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration;
bool is_const = var_decl->is_const;
bool is_extern = var_decl->is_extern;
bool is_export = var_decl->is_export;
bool is_thread_local = var_decl->threadlocal_tok != nullptr;
ZigType *explicit_type = nullptr;
if (var_decl->type) {
if (tld_var->analyzing_type) {
ErrorMsg *msg = add_node_error(g, var_decl->type,
buf_sprintf("type of '%s' depends on itself", buf_ptr(tld_var->base.name)));
emit_error_notes_for_ref_stack(g, msg);
explicit_type = g->builtin_types.entry_invalid;
} else {
tld_var->analyzing_type = true;
ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type);
explicit_type = validate_var_type(g, var_decl->type, proposed_type);
}
}
assert(!is_export || !is_extern);
VarLinkage linkage;
if (is_export) {
linkage = VarLinkageExportStrong;
} else if (is_extern) {
linkage = VarLinkageExternal;
} else {
linkage = VarLinkageInternal;
}
ConstExprValue *init_value = nullptr;
// TODO more validation for types that can't be used for export/extern variables
ZigType *implicit_type = nullptr;
if (explicit_type && explicit_type->id == ZigTypeIdInvalid) {
implicit_type = explicit_type;
} else if (var_decl->expr) {
init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol);
assert(init_value);
implicit_type = init_value->type;
if (implicit_type->id == ZigTypeIdUnreachable) {
add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable"));
implicit_type = g->builtin_types.entry_invalid;
} else if ((!is_const || linkage == VarLinkageExternal) &&
(implicit_type->id == ZigTypeIdComptimeFloat ||
implicit_type->id == ZigTypeIdComptimeInt ||
implicit_type->id == ZigTypeIdEnumLiteral))
{
add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == ZigTypeIdNull) {
add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == ZigTypeIdMetaType && !is_const) {
add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
implicit_type = g->builtin_types.entry_invalid;
}
assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime);
} else if (linkage != VarLinkageExternal) {
add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
implicit_type = g->builtin_types.entry_invalid;
}
ZigType *type = explicit_type ? explicit_type : implicit_type;
assert(type != nullptr); // should have been caught by the parser
ConstExprValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type);
tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
is_const, init_val, &tld_var->base, type);
tld_var->var->linkage = linkage;
tld_var->var->is_thread_local = is_thread_local;
if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
tld_var->var->var_type = g->builtin_types.entry_invalid;
}
if (var_decl->align_expr != nullptr) {
if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) {
tld_var->var->var_type = g->builtin_types.entry_invalid;
}
}
if (var_decl->section_expr != nullptr) {
if (var_decl->is_extern) {
add_node_error(g, var_decl->section_expr,
buf_sprintf("cannot set section of external variable '%s'", buf_ptr(var_decl->symbol)));
} else if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->section_name)) {
tld_var->section_name = nullptr;
}
}
if (is_thread_local && is_const) {
add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
}
g->global_vars.append(tld_var);
}
void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) {
if (tld->resolution != TldResolutionUnresolved)
return;
assert(tld->resolution != TldResolutionResolving);
tld->resolution = TldResolutionResolving;
g->tld_ref_source_node_stack.append(source_node);
switch (tld->id) {
case TldIdVar:
{
TldVar *tld_var = (TldVar *)tld;
resolve_decl_var(g, tld_var);
break;
}
case TldIdFn:
{
TldFn *tld_fn = (TldFn *)tld;
resolve_decl_fn(g, tld_fn);
break;
}
case TldIdContainer:
{
TldContainer *tld_container = (TldContainer *)tld;
resolve_decl_container(g, tld_container);
break;
}
case TldIdCompTime:
{
TldCompTime *tld_comptime = (TldCompTime *)tld;
resolve_decl_comptime(g, tld_comptime);
break;
}
}
tld->resolution = TldResolutionOk;
g->tld_ref_source_node_stack.pop();
}
Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name) {
// resolve all the use decls
for (size_t i = 0; i < decls_scope->use_decls.length; i += 1) {
AstNode *use_decl_node = decls_scope->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);
}
}
auto entry = decls_scope->decl_table.maybe_get(name);
return (entry == nullptr) ? nullptr : entry->value;
}
Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
while (scope) {
if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
Tld *result = find_container_decl(g, decls_scope, name);
if (result != nullptr)
return result;
}
scope = scope->parent;
}
return nullptr;
}
ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name, ScopeFnDef **crossed_fndef_scope) {
ScopeFnDef *my_crossed_fndef_scope = nullptr;
while (scope) {
if (scope->id == ScopeIdVarDecl) {
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
if (buf_eql_buf(name, &var_scope->var->name)) {
if (crossed_fndef_scope != nullptr)
*crossed_fndef_scope = my_crossed_fndef_scope;
return var_scope->var;
}
} else if (scope->id == ScopeIdDecls) {
ScopeDecls *decls_scope = (ScopeDecls *)scope;
auto entry = decls_scope->decl_table.maybe_get(name);
if (entry) {
Tld *tld = entry->value;
if (tld->id == TldIdVar) {
TldVar *tld_var = (TldVar *)tld;
if (tld_var->var) {
if (crossed_fndef_scope != nullptr)
*crossed_fndef_scope = nullptr;
return tld_var->var;
}
}
}
} else if (scope->id == ScopeIdFnDef) {
my_crossed_fndef_scope = (ScopeFnDef *)scope;
}
scope = scope->parent;
}
return nullptr;
}
ZigFn *scope_fn_entry(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdFnDef) {
ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
return fn_scope->fn_entry;
}
scope = scope->parent;
}
return nullptr;
}
ZigPackage *scope_package(Scope *scope) {
ZigType *import = get_scope_import(scope);
assert(is_top_level_struct(import));
return import->data.structure.root_struct->package;
}
TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) {
assert(enum_type->id == ZigTypeIdEnum);
if (enum_type->data.enumeration.src_field_count == 0)
return nullptr;
auto entry = enum_type->data.enumeration.fields_by_name.maybe_get(name);
if (entry == nullptr)
return nullptr;
return entry->value;
}
TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name) {
assert(type_entry->id == ZigTypeIdStruct);
assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
if (type_entry->data.structure.src_field_count == 0)
return nullptr;
auto entry = type_entry->data.structure.fields_by_name.maybe_get(name);
if (entry == nullptr)
return nullptr;
return entry->value;
}
TypeUnionField *find_union_type_field(ZigType *type_entry, Buf *name) {
assert(type_entry->id == ZigTypeIdUnion);
assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
if (type_entry->data.unionation.src_field_count == 0)
return nullptr;
auto entry = type_entry->data.unionation.fields_by_name.maybe_get(name);
if (entry == nullptr)
return nullptr;
return entry->value;
}
TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag) {
assert(type_entry->id == ZigTypeIdUnion);
assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
TypeUnionField *field = &type_entry->data.unionation.fields[i];
if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) {
return field;
}
}
return nullptr;
}
TypeEnumField *find_enum_field_by_tag(ZigType *enum_type, const BigInt *tag) {
assert(enum_type->data.enumeration.zero_bits_known);
for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *field = &enum_type->data.enumeration.fields[i];
if (bigint_cmp(&field->value, tag) == CmpEQ) {
return field;
}
}
return nullptr;
}
static bool is_container(ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdStruct:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
return true;
case ZigTypeIdPointer:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdArray:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
case ZigTypeIdVector:
return false;
}
zig_unreachable();
}
bool is_ref(ZigType *type_entry) {
return type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle;
}
bool is_array_ref(ZigType *type_entry) {
ZigType *array = is_ref(type_entry) ?
type_entry->data.pointer.child_type : type_entry;
return array->id == ZigTypeIdArray;
}
bool is_container_ref(ZigType *type_entry) {
return is_ref(type_entry) ?
is_container(type_entry->data.pointer.child_type) : is_container(type_entry);
}
ZigType *container_ref_type(ZigType *type_entry) {
assert(is_container_ref(type_entry));
return is_ref(type_entry) ?
type_entry->data.pointer.child_type : type_entry;
}
Error resolve_container_type(CodeGen *g, ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdStruct:
return resolve_struct_type(g, type_entry);
case ZigTypeIdEnum:
return resolve_enum_zero_bits(g, type_entry);
case ZigTypeIdUnion:
return resolve_union_type(g, type_entry);
case ZigTypeIdPointer:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdArray:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
case ZigTypeIdVector:
zig_unreachable();
}
zig_unreachable();
}
ZigType *get_src_ptr_type(ZigType *type) {
if (type->id == ZigTypeIdPointer) return type;
if (type->id == ZigTypeIdFn) return type;
if (type->id == ZigTypeIdPromise) return type;
if (type->id == ZigTypeIdOptional) {
if (type->data.maybe.child_type->id == ZigTypeIdPointer) {
return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type;
}
if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type;
if (type->data.maybe.child_type->id == ZigTypeIdPromise) return type->data.maybe.child_type;
}
return nullptr;
}
ZigType *get_codegen_ptr_type(ZigType *type) {
ZigType *ty = get_src_ptr_type(type);
if (ty == nullptr || !type_has_bits(ty))
return nullptr;
return ty;
}
bool type_is_nonnull_ptr(ZigType *type) {
return get_codegen_ptr_type(type) == type && !ptr_allows_addr_zero(type);
}
uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
ZigType *ptr_type = get_src_ptr_type(type);
if (ptr_type->id == ZigTypeIdPointer) {
return (ptr_type->data.pointer.explicit_alignment == 0) ?
get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
} else if (ptr_type->id == ZigTypeIdFn) {
// I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:
// "Cannot getTypeInfo() on a type that is unsized!"
// when getting the alignment of `?extern fn() void`.
// See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
} else if (ptr_type->id == ZigTypeIdPromise) {
return get_coro_frame_align_bytes(g);
} else {
zig_unreachable();
}
}
bool get_ptr_const(ZigType *type) {
ZigType *ptr_type = get_src_ptr_type(type);
if (ptr_type->id == ZigTypeIdPointer) {
return ptr_type->data.pointer.is_const;
} else if (ptr_type->id == ZigTypeIdFn) {
return true;
} else if (ptr_type->id == ZigTypeIdPromise) {
return true;
} else {
zig_unreachable();
}
}
AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index) {
if (fn_entry->param_source_nodes)
return fn_entry->param_source_nodes[index];
else if (fn_entry->proto_node)
return fn_entry->proto_node->data.fn_proto.params.at(index);
else
return nullptr;
}
static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) {
ZigType *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
Buf *param_name;
bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
if (param_decl_node && !is_var_args) {
param_name = param_decl_node->data.param_decl.name;
} else {
param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
}
if (param_name == nullptr) {
continue;
}
ZigType *param_type = param_info->type;
bool is_noalias = param_info->is_noalias;
if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
}
ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
param_name, true, create_const_runtime(param_type), nullptr, param_type);
var->src_arg_index = i;
fn_table_entry->child_scope = var->child_scope;
var->shadowable = var->shadowable || is_var_args;
if (type_has_bits(param_type)) {
fn_table_entry->variable_list.append(var);
}
}
}
bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node) {
assert(err_set_type->id == ZigTypeIdErrorSet);
ZigFn *infer_fn = err_set_type->data.error_set.infer_fn;
if (infer_fn != nullptr) {
if (infer_fn->anal_state == FnAnalStateInvalid) {
return false;
} else if (infer_fn->anal_state == FnAnalStateReady) {
analyze_fn_body(g, infer_fn);
if (err_set_type->data.error_set.infer_fn != nullptr) {
assert(g->errors.length != 0);
return false;
}
} else {
add_node_error(g, source_node,
buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
return false;
}
}
return true;
}
void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) {
ZigType *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
ZigType *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable,
&fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
fn_table_entry->src_implicit_return_type = block_return_type;
if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) {
assert(g->errors.length > 0);
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) {
ZigType *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type;
if (return_err_set_type->data.error_set.infer_fn != nullptr) {
ZigType *inferred_err_set_type;
if (fn_table_entry->src_implicit_return_type->id == ZigTypeIdErrorSet) {
inferred_err_set_type = fn_table_entry->src_implicit_return_type;
} else if (fn_table_entry->src_implicit_return_type->id == ZigTypeIdErrorUnion) {
inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_type;
} else {
add_node_error(g, return_type_node,
buf_sprintf("function with inferred error set must return at least one possible error"));
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
if (inferred_err_set_type->data.error_set.infer_fn != nullptr) {
if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
}
return_err_set_type->data.error_set.infer_fn = nullptr;
if (type_is_global_error_set(inferred_err_set_type)) {
return_err_set_type->data.error_set.err_count = UINT32_MAX;
} else {
return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
if (inferred_err_set_type->data.error_set.err_count > 0) {
return_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);
for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
}
}
}
}
}
if (g->verbose_ir) {
fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn_table_entry->symbol_name));
ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4);
fprintf(stderr, "}\n");
}
fn_table_entry->anal_state = FnAnalStateComplete;
}
static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
assert(fn_table_entry->anal_state != FnAnalStateProbing);
if (fn_table_entry->anal_state != FnAnalStateReady)
return;
fn_table_entry->anal_state = FnAnalStateProbing;
AstNode *return_type_node = (fn_table_entry->proto_node != nullptr) ?
fn_table_entry->proto_node->data.fn_proto.return_type : fn_table_entry->fndef_scope->base.source_node;
assert(fn_table_entry->fndef_scope);
if (!fn_table_entry->child_scope)
fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
define_local_param_variables(g, fn_table_entry);
ZigType *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
ir_gen_fn(g, fn_table_entry);
if (fn_table_entry->ir_executable.invalid) {
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
if (g->verbose_ir) {
fprintf(stderr, "\n");
ast_render(stderr, fn_table_entry->body_node, 4);
fprintf(stderr, "\n{ // (IR)\n");
ir_print(g, stderr, &fn_table_entry->ir_executable, 4);
fprintf(stderr, "}\n");
}
analyze_fn_ir(g, fn_table_entry, return_type_node);
}
static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) {
if (src_use_node->data.use.resolution == TldResolutionUnresolved) {
preview_use_decl(g, src_use_node);
}
ConstExprValue *use_target_value = src_use_node->data.use.using_namespace_value;
if (type_is_invalid(use_target_value->type)) {
get_container_scope(dst_use_node->owner)->any_imports_failed = true;
return;
}
dst_use_node->data.use.resolution = TldResolutionOk;
assert(use_target_value->special != ConstValSpecialRuntime);
ZigType *target_import = use_target_value->data.x_type;
assert(target_import);
if (target_import->id != ZigTypeIdStruct) {
add_node_error(g, dst_use_node,
buf_sprintf("expected struct, found '%s'", buf_ptr(&target_import->name)));
get_container_scope(dst_use_node->owner)->any_imports_failed = true;
return;
}
if (get_container_scope(target_import)->any_imports_failed) {
get_container_scope(dst_use_node->owner)->any_imports_failed = true;
}
auto it = get_container_scope(target_import)->decl_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
Tld *target_tld = entry->value;
if (target_tld->import != target_import ||
target_tld->visib_mod == VisibModPrivate)
{
continue;
}
Buf *target_tld_name = entry->key;
auto existing_entry = get_container_scope(dst_use_node->owner)->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 < get_container_scope(target_import)->use_decls.length; i += 1) {
AstNode *use_decl_node = get_container_scope(target_import)->use_decls.at(i);
if (use_decl_node->data.use.visib_mod != VisibModPrivate)
add_symbols_from_import(g, use_decl_node, dst_use_node);
}
}
void resolve_use_decl(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeUse);
if (node->data.use.resolution == TldResolutionOk ||
node->data.use.resolution == TldResolutionInvalid)
{
return;
}
add_symbols_from_import(g, node, node);
}
void preview_use_decl(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeUse);
if (node->data.use.resolution == TldResolutionOk ||
node->data.use.resolution == TldResolutionInvalid)
{
return;
}
node->data.use.resolution = TldResolutionResolving;
ConstExprValue *result = analyze_const_value(g, &get_container_scope(node->owner)->base,
node->data.use.expr, g->builtin_types.entry_type, nullptr);
if (type_is_invalid(result->type))
get_container_scope(node->owner)->any_imports_failed = true;
node->data.use.using_namespace_value = result;
}
ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code,
SourceKind source_kind)
{
if (g->verbose_tokenize) {
fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(resolved_path));
fprintf(stderr, "----------------\n");
fprintf(stderr, "%s\n", buf_ptr(source_code));
fprintf(stderr, "\nTokens:\n");
fprintf(stderr, "---------\n");
}
Tokenization tokenization = {0};
tokenize(source_code, &tokenization);
if (tokenization.err) {
ErrorMsg *err = err_msg_create_with_line(resolved_path, tokenization.err_line, tokenization.err_column,
source_code, tokenization.line_offsets, tokenization.err);
print_err_msg(err, g->err_color);
exit(1);
}
if (g->verbose_tokenize) {
print_tokens(source_code, tokenization.tokens);
fprintf(stderr, "\nAST:\n");
fprintf(stderr, "------\n");
}
Buf *src_dirname = buf_alloc();
Buf *src_basename = buf_alloc();
os_path_split(resolved_path, src_dirname, src_basename);
Buf noextname = BUF_INIT;
os_path_extname(resolved_path, &noextname, nullptr);
Buf *pkg_root_src_dir = &package->root_src_dir;
Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1);
Buf namespace_name = BUF_INIT;
buf_init_from_buf(&namespace_name, &package->pkg_path);
if (source_kind == SourceKindNonRoot) {
assert(buf_starts_with_buf(resolved_path, &resolved_root_src_dir));
if (buf_len(&namespace_name) != 0) buf_append_char(&namespace_name, NAMESPACE_SEP_CHAR);
buf_append_mem(&namespace_name, buf_ptr(&noextname) + buf_len(&resolved_root_src_dir) + 1,
buf_len(&noextname) - (buf_len(&resolved_root_src_dir) + 1));
buf_replace(&namespace_name, ZIG_OS_SEP_CHAR, NAMESPACE_SEP_CHAR);
}
Buf *bare_name = buf_alloc();
os_path_extname(src_basename, bare_name, nullptr);
RootStruct *root_struct = allocate<RootStruct>(1);
root_struct->package = package;
root_struct->source_code = source_code;
root_struct->line_offsets = tokenization.line_offsets;
root_struct->path = resolved_path;
root_struct->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
ZigType *import_entry = get_root_container_type(g, buf_ptr(&namespace_name), bare_name, root_struct);
if (source_kind == SourceKindRoot) {
assert(g->root_import == nullptr);
g->root_import = import_entry;
}
g->import_table.put(resolved_path, import_entry);
AstNode *root_node = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color);
assert(root_node != nullptr);
assert(root_node->type == NodeTypeContainerDecl);
import_entry->data.structure.decl_node = root_node;
import_entry->data.structure.decls_scope->base.source_node = root_node;
if (g->verbose_ast) {
ast_print(stderr, root_node, 0);
}
if (source_kind == SourceKindRoot || package == g->panic_package) {
// Look for panic and main
for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) {
AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i);
if (top_level_decl->type == NodeTypeFnDef) {
AstNode *proto_node = top_level_decl->data.fn_def.fn_proto;
assert(proto_node->type == NodeTypeFnProto);
Buf *proto_name = proto_node->data.fn_proto.name;
bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub);
if (is_pub) {
if (buf_eql_str(proto_name, "main")) {
g->have_pub_main = true;
g->subsystem = TargetSubsystemConsole;
} else if (buf_eql_str(proto_name, "panic")) {
g->have_pub_panic = true;
}
}
}
}
}
for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) {
AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i);
scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl);
}
TldContainer *tld_container = allocate<TldContainer>(1);
init_tld(&tld_container->base, TldIdContainer, &namespace_name, VisibModPub, root_node, nullptr);
tld_container->type_entry = import_entry;
tld_container->decls_scope = import_entry->data.structure.decls_scope;
g->resolve_queue.append(&tld_container->base);
return import_entry;
}
void semantic_analyze(CodeGen *g) {
while (g->resolve_queue_index < g->resolve_queue.length ||
g->fn_defs_index < g->fn_defs.length ||
g->use_queue_index < g->use_queue.length)
{
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);
resolve_use_decl(g, use_decl_node);
}
for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
AstNode *source_node = nullptr;
resolve_top_level_decl(g, tld, source_node);
}
for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index);
analyze_fn_body(g, fn_entry);
}
}
}
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
assert(size_in_bits <= 65535);
TypeId type_id = {};
type_id.id = ZigTypeIdInt;
type_id.data.integer.is_signed = is_signed;
type_id.data.integer.bit_count = size_in_bits;
{
auto entry = g->type_table.maybe_get(type_id);
if (entry)
return entry->value;
}
ZigType *new_entry = make_int_type(g, is_signed, size_in_bits);
g->type_table.put(type_id, new_entry);
return new_entry;
}
bool is_valid_vector_elem_type(ZigType *elem_type) {
return elem_type->id == ZigTypeIdInt ||
elem_type->id == ZigTypeIdFloat ||
get_codegen_ptr_type(elem_type) != nullptr;
}
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
assert(is_valid_vector_elem_type(elem_type));
TypeId type_id = {};
type_id.id = ZigTypeIdVector;
type_id.data.vector.len = len;
type_id.data.vector.elem_type = elem_type;
{
auto entry = g->type_table.maybe_get(type_id);
if (entry)
return entry->value;
}
ZigType *entry = new_type_table_entry(ZigTypeIdVector);
if ((len != 0) && type_has_bits(elem_type)) {
// Vectors can only be ints, floats, or pointers. ints and floats have trivially resolvable
// llvm type refs. pointers we will use usize instead.
LLVMTypeRef example_vector_llvm_type;
if (elem_type->id == ZigTypeIdPointer) {
example_vector_llvm_type = LLVMVectorType(g->builtin_types.entry_usize->llvm_type, len);
} else {
example_vector_llvm_type = LLVMVectorType(elem_type->llvm_type, len);
}
assert(example_vector_llvm_type != nullptr);
entry->size_in_bits = elem_type->size_in_bits * len;
entry->abi_size = LLVMABISizeOfType(g->target_data_ref, example_vector_llvm_type);
entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, example_vector_llvm_type);
}
entry->data.vector.len = len;
entry->data.vector.elem_type = elem_type;
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name));
g->type_table.put(type_id, entry);
return entry;
}
ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
return &g->builtin_types.entry_c_int[c_int_type];
}
ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type) {
return *get_c_int_type_ptr(g, c_int_type);
}
bool handle_is_ptr(ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdEnum:
case ZigTypeIdPromise:
case ZigTypeIdVector:
return false;
case ZigTypeIdArray:
case ZigTypeIdStruct:
return type_has_bits(type_entry);
case ZigTypeIdErrorUnion:
return type_has_bits(type_entry->data.error_union.payload_type);
case ZigTypeIdOptional:
return type_has_bits(type_entry->data.maybe.child_type) &&
!type_is_nonnull_ptr(type_entry->data.maybe.child_type) &&
type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet;
case ZigTypeIdUnion:
return type_has_bits(type_entry) && type_entry->data.unionation.gen_field_count != 0;
}
zig_unreachable();
}
static uint32_t hash_ptr(void *ptr) {
return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX);
}
static uint32_t hash_size(size_t x) {
return (uint32_t)(x % UINT32_MAX);
}
uint32_t fn_table_entry_hash(ZigFn* value) {
return ptr_hash(value);
}
bool fn_table_entry_eql(ZigFn *a, ZigFn *b) {
return ptr_eq(a, b);
}
uint32_t fn_type_id_hash(FnTypeId *id) {
uint32_t result = 0;
result += ((uint32_t)(id->cc)) * (uint32_t)3349388391;
result += id->is_var_args ? (uint32_t)1931444534 : 0;
result += hash_ptr(id->return_type);
result += hash_ptr(id->async_allocator_type);
result += id->alignment * 0xd3b3f3e2;
for (size_t i = 0; i < id->param_count; i += 1) {
FnTypeParamInfo *info = &id->param_info[i];
result += info->is_noalias ? (uint32_t)892356923 : 0;
result += hash_ptr(info->type);
}
return result;
}
bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
if (a->cc != b->cc ||
a->return_type != b->return_type ||
a->is_var_args != b->is_var_args ||
a->param_count != b->param_count ||
a->alignment != b->alignment ||
a->async_allocator_type != b->async_allocator_type)
{
return false;
}
for (size_t i = 0; i < a->param_count; i += 1) {
FnTypeParamInfo *a_param_info = &a->param_info[i];
FnTypeParamInfo *b_param_info = &b->param_info[i];
if (a_param_info->type != b_param_info->type ||
a_param_info->is_noalias != b_param_info->is_noalias)
{
return false;
}
}
return true;
}
static uint32_t hash_const_val_error_set(ConstExprValue *const_val) {
assert(const_val->data.x_err_set != nullptr);
return const_val->data.x_err_set->value ^ 2630160122;
}
static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
uint32_t hash_val = 0;
switch (const_val->data.x_ptr.mut) {
case ConstPtrMutRuntimeVar:
hash_val += (uint32_t)3500721036;
break;
case ConstPtrMutComptimeConst:
hash_val += (uint32_t)4214318515;
break;
case ConstPtrMutComptimeVar:
hash_val += (uint32_t)1103195694;
break;
}
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
hash_val += (uint32_t)2478261866;
hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
return hash_val;
case ConstPtrSpecialBaseArray:
hash_val += (uint32_t)1764906839;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
return hash_val;
case ConstPtrSpecialBaseStruct:
hash_val += (uint32_t)3518317043;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
return hash_val;
case ConstPtrSpecialBaseErrorUnionCode:
hash_val += (uint32_t)2994743799;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val);
return hash_val;
case ConstPtrSpecialBaseErrorUnionPayload:
hash_val += (uint32_t)3456080131;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val);
return hash_val;
case ConstPtrSpecialBaseOptionalPayload:
hash_val += (uint32_t)3163140517;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val);
return hash_val;
case ConstPtrSpecialHardCodedAddr:
hash_val += (uint32_t)4048518294;
hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
return hash_val;
case ConstPtrSpecialDiscard:
hash_val += 2010123162;
return hash_val;
case ConstPtrSpecialFunction:
hash_val += (uint32_t)2590901619;
hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
return hash_val;
case ConstPtrSpecialNull:
hash_val += (uint32_t)1486246455;
return hash_val;
}
zig_unreachable();
}
static uint32_t hash_const_val(ConstExprValue *const_val) {
assert(const_val->special == ConstValSpecialStatic);
switch (const_val->type->id) {
case ZigTypeIdOpaque:
zig_unreachable();
case ZigTypeIdBool:
return const_val->data.x_bool ? (uint32_t)127863866 : (uint32_t)215080464;
case ZigTypeIdMetaType:
return hash_ptr(const_val->data.x_type);
case ZigTypeIdVoid:
return (uint32_t)4149439618;
case ZigTypeIdInt:
case ZigTypeIdComptimeInt:
{
uint32_t result = 1331471175;
for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) {
uint64_t digit = bigint_ptr(&const_val->data.x_bigint)[i];
result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result);
}
return result;
}
case ZigTypeIdEnumLiteral:
return buf_hash(const_val->data.x_enum_literal) * 2691276464;
case ZigTypeIdEnum:
{
uint32_t result = 31643936;
for (size_t i = 0; i < const_val->data.x_enum_tag.digit_count; i += 1) {
uint64_t digit = bigint_ptr(&const_val->data.x_enum_tag)[i];
result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result);
}
return result;
}
case ZigTypeIdFloat:
switch (const_val->type->data.floating.bit_count) {
case 16:
{
uint16_t result;
static_assert(sizeof(result) == sizeof(const_val->data.x_f16), "");
memcpy(&result, &const_val->data.x_f16, sizeof(result));
return result * 65537u;
}
case 32:
{
uint32_t result;
memcpy(&result, &const_val->data.x_f32, 4);
return result ^ 4084870010;
}
case 64:
{
uint32_t ints[2];
memcpy(&ints[0], &const_val->data.x_f64, 8);
return ints[0] ^ ints[1] ^ 0x22ed43c6;
}
case 128:
{
uint32_t ints[4];
memcpy(&ints[0], &const_val->data.x_f128, 16);
return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xb5ffef27;
}
default:
zig_unreachable();
}
case ZigTypeIdComptimeFloat:
{
float128_t f128 = bigfloat_to_f128(&const_val->data.x_bigfloat);
uint32_t ints[4];
memcpy(&ints[0], &f128, 16);
return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xed8b3dfb;
}
case ZigTypeIdArgTuple:
return (uint32_t)const_val->data.x_arg_tuple.start_index * (uint32_t)281907309 +
(uint32_t)const_val->data.x_arg_tuple.end_index * (uint32_t)2290442768;
case ZigTypeIdFn:
assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
case ZigTypeIdPointer:
return hash_const_val_ptr(const_val);
case ZigTypeIdPromise:
// TODO better hashing algorithm
return 223048345;
case ZigTypeIdUndefined:
return 162837799;
case ZigTypeIdNull:
return 844854567;
case ZigTypeIdArray:
// TODO better hashing algorithm
return 1166190605;
case ZigTypeIdStruct:
// TODO better hashing algorithm
return 1532530855;
case ZigTypeIdUnion:
// TODO better hashing algorithm
return 2709806591;
case ZigTypeIdOptional:
if (get_codegen_ptr_type(const_val->type) != nullptr) {
return hash_const_val_ptr(const_val) * 1992916303;
} else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) {
return hash_const_val_error_set(const_val) * 3147031929;
} else {
if (const_val->data.x_optional) {
return hash_const_val(const_val->data.x_optional) * 1992916303;
} else {
return 4016830364;
}
}
case ZigTypeIdErrorUnion:
// TODO better hashing algorithm
return 3415065496;
case ZigTypeIdErrorSet:
return hash_const_val_error_set(const_val);
case ZigTypeIdVector:
// TODO better hashing algorithm
return 3647867726;
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
zig_unreachable();
}
zig_unreachable();
}
uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) {
uint32_t result = 0;
result += hash_ptr(id->fn_entry);
for (size_t i = 0; i < id->param_count; i += 1) {
ConstExprValue *generic_param = &id->params[i];
if (generic_param->special != ConstValSpecialRuntime) {
result += hash_const_val(generic_param);
result += hash_ptr(generic_param->type);
}
}
return result;
}
bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
assert(a->fn_entry);
if (a->fn_entry != b->fn_entry) return false;
if (a->param_count != b->param_count) return false;
for (size_t i = 0; i < a->param_count; i += 1) {
ConstExprValue *a_val = &a->params[i];
ConstExprValue *b_val = &b->params[i];
if (a_val->type != b_val->type) return false;
if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) {
assert(a_val->special == ConstValSpecialStatic);
assert(b_val->special == ConstValSpecialStatic);
if (!const_values_equal(a->fn_entry->codegen, a_val, b_val)) {
return false;
}
} else {
assert(a_val->special == ConstValSpecialRuntime && b_val->special == ConstValSpecialRuntime);
}
}
return true;
}
static bool can_mutate_comptime_var_state(ConstExprValue *value) {
assert(value != nullptr);
switch (value->type->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdFn:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
return false;
case ZigTypeIdPointer:
return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
case ZigTypeIdArray:
if (value->type->data.array.len == 0)
return false;
switch (value->data.x_array.special) {
case ConstArraySpecialUndef:
case ConstArraySpecialBuf:
return false;
case ConstArraySpecialNone:
for (uint32_t i = 0; i < value->type->data.array.len; i += 1) {
if (can_mutate_comptime_var_state(&value->data.x_array.data.s_none.elements[i]))
return true;
}
return false;
}
zig_unreachable();
case ZigTypeIdStruct:
for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) {
if (can_mutate_comptime_var_state(&value->data.x_struct.fields[i]))
return true;
}
return false;
case ZigTypeIdOptional:
if (get_codegen_ptr_type(value->type) != nullptr)
return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
if (value->data.x_optional == nullptr)
return false;
return can_mutate_comptime_var_state(value->data.x_optional);
case ZigTypeIdErrorUnion:
if (value->data.x_err_union.error_set->data.x_err_set != nullptr)
return false;
assert(value->data.x_err_union.payload != nullptr);
return can_mutate_comptime_var_state(value->data.x_err_union.payload);
case ZigTypeIdUnion:
return can_mutate_comptime_var_state(value->data.x_union.payload);
case ZigTypeIdArgTuple:
zig_panic("TODO var args at comptime is currently not supported");
}
zig_unreachable();
}
static bool return_type_is_cacheable(ZigType *return_type) {
switch (return_type->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdFn:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdPointer:
case ZigTypeIdVector:
return true;
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdUnion:
return false;
case ZigTypeIdOptional:
return return_type_is_cacheable(return_type->data.maybe.child_type);
case ZigTypeIdErrorUnion:
return return_type_is_cacheable(return_type->data.error_union.payload_type);
case ZigTypeIdArgTuple:
zig_panic("TODO var args at comptime is currently not supported");
}
zig_unreachable();
}
bool fn_eval_cacheable(Scope *scope, ZigType *return_type) {
if (!return_type_is_cacheable(return_type))
return false;
while (scope) {
if (scope->id == ScopeIdVarDecl) {
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
if (type_is_invalid(var_scope->var->var_type))
return false;
if (can_mutate_comptime_var_state(var_scope->var->const_value))
return false;
} else if (scope->id == ScopeIdFnDef) {
return true;
} else {
zig_unreachable();
}
scope = scope->parent;
}
zig_unreachable();
}
uint32_t fn_eval_hash(Scope* scope) {
uint32_t result = 0;
while (scope) {
if (scope->id == ScopeIdVarDecl) {
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
result += hash_const_val(var_scope->var->const_value);
} else if (scope->id == ScopeIdFnDef) {
ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
result += hash_ptr(fn_scope->fn_entry);
return result;
} else {
zig_unreachable();
}
scope = scope->parent;
}
zig_unreachable();
}
bool fn_eval_eql(Scope *a, Scope *b) {
assert(a->codegen != nullptr);
assert(b->codegen != nullptr);
while (a && b) {
if (a->id != b->id)
return false;
if (a->id == ScopeIdVarDecl) {
ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a;
ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b;
if (a_var_scope->var->var_type != b_var_scope->var->var_type)
return false;
if (a_var_scope->var->var_type == a_var_scope->var->const_value->type &&
b_var_scope->var->var_type == b_var_scope->var->const_value->type)
{
if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value))
return false;
} else {
zig_panic("TODO comptime ptr reinterpret for fn_eval_eql");
}
} else if (a->id == ScopeIdFnDef) {
ScopeFnDef *a_fn_scope = (ScopeFnDef *)a;
ScopeFnDef *b_fn_scope = (ScopeFnDef *)b;
if (a_fn_scope->fn_entry != b_fn_scope->fn_entry)
return false;
return true;
} else {
zig_unreachable();
}
a = a->parent;
b = b->parent;
}
return false;
}
// Whether the type has bits at runtime.
bool type_has_bits(ZigType *type_entry) {
assert(type_entry != nullptr);
assert(!type_is_invalid(type_entry));
assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
return type_entry->abi_size != 0;
}
// Whether you can infer the value based solely on the type.
OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
assert(type_entry != nullptr);
if (type_entry->one_possible_value != OnePossibleValueInvalid)
return type_entry->one_possible_value;
Error err;
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
return OnePossibleValueInvalid;
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdOpaque:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdMetaType:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOptional:
case ZigTypeIdFn:
case ZigTypeIdBool:
case ZigTypeIdFloat:
case ZigTypeIdPromise:
case ZigTypeIdErrorUnion:
return OnePossibleValueNo;
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdVoid:
case ZigTypeIdUnreachable:
return OnePossibleValueYes;
case ZigTypeIdArray:
if (type_entry->data.array.len == 0)
return OnePossibleValueYes;
return type_has_one_possible_value(g, type_entry->data.array.child_type);
case ZigTypeIdStruct:
for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
TypeStructField *field = &type_entry->data.structure.fields[i];
switch (type_has_one_possible_value(g, field->type_entry)) {
case OnePossibleValueInvalid:
return OnePossibleValueInvalid;
case OnePossibleValueNo:
return OnePossibleValueNo;
case OnePossibleValueYes:
continue;
}
}
return OnePossibleValueYes;
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdInt:
case ZigTypeIdVector:
return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes;
case ZigTypeIdPointer: {
ZigType *elem_type = type_entry->data.pointer.child_type;
// If the recursive function call asks, then we are not one possible value.
type_entry->one_possible_value = OnePossibleValueNo;
// Now update it to be the value of the recursive call.
type_entry->one_possible_value = type_has_one_possible_value(g, elem_type);
return type_entry->one_possible_value;
}
case ZigTypeIdUnion:
if (type_entry->data.unionation.src_field_count > 1)
return OnePossibleValueNo;
return type_has_one_possible_value(g, type_entry->data.unionation.fields[0].type_entry);
}
zig_unreachable();
}
ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) {
Error err;
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
return ReqCompTimeInvalid;
switch (type_entry->id) {
case ZigTypeIdInvalid:
case ZigTypeIdOpaque:
zig_unreachable();
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdMetaType:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
return ReqCompTimeYes;
case ZigTypeIdArray:
return type_requires_comptime(g, type_entry->data.array.child_type);
case ZigTypeIdStruct:
return type_entry->data.structure.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo;
case ZigTypeIdUnion:
return type_entry->data.unionation.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo;
case ZigTypeIdOptional:
return type_requires_comptime(g, type_entry->data.maybe.child_type);
case ZigTypeIdErrorUnion:
return type_requires_comptime(g, type_entry->data.error_union.payload_type);
case ZigTypeIdPointer:
if (type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) {
return ReqCompTimeNo;
} else {
return type_requires_comptime(g, type_entry->data.pointer.child_type);
}
case ZigTypeIdFn:
return type_entry->data.fn.is_generic ? ReqCompTimeYes : ReqCompTimeNo;
case ZigTypeIdEnum:
case ZigTypeIdErrorSet:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdVoid:
case ZigTypeIdUnreachable:
case ZigTypeIdPromise:
return ReqCompTimeNo;
}
zig_unreachable();
}
void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
auto entry = g->string_literals_table.maybe_get(str);
if (entry != nullptr) {
*const_val = *entry->value;
return;
}
const_val->special = ConstValSpecialStatic;
const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str));
const_val->data.x_array.special = ConstArraySpecialBuf;
const_val->data.x_array.data.s_buf = str;
g->string_literals_table.put(str, const_val);
}
ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str) {
ConstExprValue *const_val = create_const_vals(1);
init_const_str_lit(g, const_val, str);
return const_val;
}
void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
// first we build the underlying array
size_t len_with_null = buf_len(str) + 1;
ConstExprValue *array_val = create_const_vals(1);
array_val->special = ConstValSpecialStatic;
array_val->type = get_array_type(g, g->builtin_types.entry_u8, len_with_null);
// TODO buf optimization
array_val->data.x_array.data.s_none.elements = create_const_vals(len_with_null);
for (size_t i = 0; i < buf_len(str); i += 1) {
ConstExprValue *this_char = &array_val->data.x_array.data.s_none.elements[i];
this_char->special = ConstValSpecialStatic;
this_char->type = g->builtin_types.entry_u8;
bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]);
}
ConstExprValue *null_char = &array_val->data.x_array.data.s_none.elements[len_with_null - 1];
null_char->special = ConstValSpecialStatic;
null_char->type = g->builtin_types.entry_u8;
bigint_init_unsigned(&null_char->data.x_bigint, 0);
// then make the pointer point to it
const_val->special = ConstValSpecialStatic;
// TODO make this `[*]null u8` instead of `[*]u8`
const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, 0, 0, 0, false);
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
const_val->data.x_ptr.data.base_array.array_val = array_val;
const_val->data.x_ptr.data.base_array.elem_index = 0;
const_val->data.x_ptr.data.base_array.is_cstr = true;
}
ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) {
ConstExprValue *const_val = create_const_vals(1);
init_const_c_str_lit(g, const_val, str);
return const_val;
}
void init_const_bigint(ConstExprValue *const_val, ZigType *type, const BigInt *bigint) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
bigint_init_bigint(&const_val->data.x_bigint, bigint);
}
ConstExprValue *create_const_bigint(ZigType *type, const BigInt *bigint) {
ConstExprValue *const_val = create_const_vals(1);
init_const_bigint(const_val, type, bigint);
return const_val;
}
void init_const_unsigned_negative(ConstExprValue *const_val, ZigType *type, uint64_t x, bool negative) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
bigint_init_unsigned(&const_val->data.x_bigint, x);
const_val->data.x_bigint.is_negative = negative;
}
ConstExprValue *create_const_unsigned_negative(ZigType *type, uint64_t x, bool negative) {
ConstExprValue *const_val = create_const_vals(1);
init_const_unsigned_negative(const_val, type, x, negative);
return const_val;
}
void init_const_usize(CodeGen *g, ConstExprValue *const_val, uint64_t x) {
return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false);
}
ConstExprValue *create_const_usize(CodeGen *g, uint64_t x) {
return create_const_unsigned_negative(g->builtin_types.entry_usize, x, false);
}
void init_const_signed(ConstExprValue *const_val, ZigType *type, int64_t x) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
bigint_init_signed(&const_val->data.x_bigint, x);
}
ConstExprValue *create_const_signed(ZigType *type, int64_t x) {
ConstExprValue *const_val = create_const_vals(1);
init_const_signed(const_val, type, x);
return const_val;
}
void init_const_float(ConstExprValue *const_val, ZigType *type, double value) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
if (type->id == ZigTypeIdComptimeFloat) {
bigfloat_init_64(&const_val->data.x_bigfloat, value);
} else if (type->id == ZigTypeIdFloat) {
switch (type->data.floating.bit_count) {
case 16:
const_val->data.x_f16 = zig_double_to_f16(value);
break;
case 32:
const_val->data.x_f32 = value;
break;
case 64:
const_val->data.x_f64 = value;
break;
case 128:
// if we need this, we should add a function that accepts a float128_t param
zig_unreachable();
default:
zig_unreachable();
}
} else {
zig_unreachable();
}
}
ConstExprValue *create_const_float(ZigType *type, double value) {
ConstExprValue *const_val = create_const_vals(1);
init_const_float(const_val, type, value);
return const_val;
}
void init_const_enum(ConstExprValue *const_val, ZigType *type, const BigInt *tag) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;
bigint_init_bigint(&const_val->data.x_enum_tag, tag);
}
ConstExprValue *create_const_enum(ZigType *type, const BigInt *tag) {
ConstExprValue *const_val = create_const_vals(1);
init_const_enum(const_val, type, tag);
return const_val;
}
void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value) {
const_val->special = ConstValSpecialStatic;
const_val->type = g->builtin_types.entry_bool;
const_val->data.x_bool = value;
}
ConstExprValue *create_const_bool(CodeGen *g, bool value) {
ConstExprValue *const_val = create_const_vals(1);
init_const_bool(g, const_val, value);
return const_val;
}
void init_const_runtime(ConstExprValue *const_val, ZigType *type) {
const_val->special = ConstValSpecialRuntime;
const_val->type = type;
}
ConstExprValue *create_const_runtime(ZigType *type) {
ConstExprValue *const_val = create_const_vals(1);
init_const_runtime(const_val, type);
return const_val;
}
void init_const_type(CodeGen *g, ConstExprValue *const_val, ZigType *type_value) {
const_val->special = ConstValSpecialStatic;
const_val->type = g->builtin_types.entry_type;
const_val->data.x_type = type_value;
}
ConstExprValue *create_const_type(CodeGen *g, ZigType *type_value) {
ConstExprValue *const_val = create_const_vals(1);
init_const_type(g, const_val, type_value);
return const_val;
}
void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
size_t start, size_t len, bool is_const)
{
assert(array_val->type->id == ZigTypeIdArray);
ZigType *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type,
is_const, false, PtrLenUnknown, 0, 0, 0, false);
const_val->special = ConstValSpecialStatic;
const_val->type = get_slice_type(g, ptr_type);
const_val->data.x_struct.fields = create_const_vals(2);
init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const,
PtrLenUnknown);
init_const_usize(g, &const_val->data.x_struct.fields[slice_len_index], len);
}
ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t start, size_t len, bool is_const) {
ConstExprValue *const_val = create_const_vals(1);
init_const_slice(g, const_val, array_val, start, len, is_const);
return const_val;
}
void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
size_t elem_index, bool is_const, PtrLen ptr_len)
{
assert(array_val->type->id == ZigTypeIdArray);
ZigType *child_type = array_val->type->data.array.child_type;
const_val->special = ConstValSpecialStatic;
const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false,
ptr_len, 0, 0, 0, false);
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
const_val->data.x_ptr.data.base_array.array_val = array_val;
const_val->data.x_ptr.data.base_array.elem_index = elem_index;
}
ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const,
PtrLen ptr_len)
{
ConstExprValue *const_val = create_const_vals(1);
init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len);
return const_val;
}
void init_const_ptr_ref(CodeGen *g, ConstExprValue *const_val, ConstExprValue *pointee_val, bool is_const) {
const_val->special = ConstValSpecialStatic;
const_val->type = get_pointer_to_type(g, pointee_val->type, is_const);
const_val->data.x_ptr.special = ConstPtrSpecialRef;
const_val->data.x_ptr.data.ref.pointee = pointee_val;
}
ConstExprValue *create_const_ptr_ref(CodeGen *g, ConstExprValue *pointee_val, bool is_const) {
ConstExprValue *const_val = create_const_vals(1);
init_const_ptr_ref(g, const_val, pointee_val, is_const);
return const_val;
}
void init_const_ptr_hard_coded_addr(CodeGen *g, ConstExprValue *const_val, ZigType *pointee_type,
size_t addr, bool is_const)
{
const_val->special = ConstValSpecialStatic;
const_val->type = get_pointer_to_type(g, pointee_type, is_const);
const_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
const_val->data.x_ptr.data.hard_coded_addr.addr = addr;
}
ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type,
size_t addr, bool is_const)
{
ConstExprValue *const_val = create_const_vals(1);
init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const);
return const_val;
}
void init_const_arg_tuple(CodeGen *g, ConstExprValue *const_val, size_t arg_index_start, size_t arg_index_end) {
const_val->special = ConstValSpecialStatic;
const_val->type = g->builtin_types.entry_arg_tuple;
const_val->data.x_arg_tuple.start_index = arg_index_start;
const_val->data.x_arg_tuple.end_index = arg_index_end;
}
ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_t arg_index_end) {
ConstExprValue *const_val = create_const_vals(1);
init_const_arg_tuple(g, const_val, arg_index_start, arg_index_end);
return const_val;
}
void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
Error err;
ZigType *wanted_type = const_val->type;
if (wanted_type->id == ZigTypeIdArray) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_array.special = ConstArraySpecialUndef;
} else if (wanted_type->id == ZigTypeIdStruct) {
if ((err = ensure_complete_type(g, wanted_type))) {
return;
}
const_val->special = ConstValSpecialStatic;
size_t field_count = wanted_type->data.structure.src_field_count;
const_val->data.x_struct.fields = create_const_vals(field_count);
for (size_t i = 0; i < field_count; i += 1) {
ConstExprValue *field_val = &const_val->data.x_struct.fields[i];
field_val->type = wanted_type->data.structure.fields[i].type_entry;
assert(field_val->type);
init_const_undefined(g, field_val);
ConstParent *parent = get_const_val_parent(g, field_val);
if (parent != nullptr) {
parent->id = ConstParentIdStruct;
parent->data.p_struct.struct_val = const_val;
parent->data.p_struct.field_index = i;
}
}
} else {
const_val->special = ConstValSpecialUndef;
}
}
ConstExprValue *create_const_vals(size_t count) {
ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count);
ConstExprValue *vals = allocate<ConstExprValue>(count);
for (size_t i = 0; i < count; i += 1) {
vals[i].global_refs = &global_refs[i];
}
return vals;
}
Error ensure_complete_type(CodeGen *g, ZigType *type_entry) {
return type_resolve(g, type_entry, ResolveStatusSizeKnown);
}
Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) {
if (type_is_invalid(ty))
return ErrorSemanticAnalyzeFail;
switch (status) {
case ResolveStatusUnstarted:
return ErrorNone;
case ResolveStatusInvalid:
zig_unreachable();
case ResolveStatusZeroBitsKnown:
if (ty->id == ZigTypeIdStruct) {
return resolve_struct_zero_bits(g, ty);
} else if (ty->id == ZigTypeIdEnum) {
return resolve_enum_zero_bits(g, ty);
} else if (ty->id == ZigTypeIdUnion) {
return resolve_union_zero_bits(g, ty);
}
return ErrorNone;
case ResolveStatusAlignmentKnown:
if (ty->id == ZigTypeIdStruct) {
return resolve_struct_alignment(g, ty);
} else if (ty->id == ZigTypeIdEnum) {
return resolve_enum_zero_bits(g, ty);
} else if (ty->id == ZigTypeIdUnion) {
return resolve_union_alignment(g, ty);
}
return ErrorNone;
case ResolveStatusSizeKnown:
if (ty->id == ZigTypeIdStruct) {
return resolve_struct_type(g, ty);
} else if (ty->id == ZigTypeIdEnum) {
return resolve_enum_zero_bits(g, ty);
} else if (ty->id == ZigTypeIdUnion) {
return resolve_union_type(g, ty);
}
return ErrorNone;
case ResolveStatusLLVMFwdDecl:
case ResolveStatusLLVMFull:
resolve_llvm_types(g, ty, status);
return ErrorNone;
}
zig_unreachable();
}
bool ir_get_var_is_comptime(ZigVar *var) {
// The is_comptime field can be left null, which means not comptime.
if (var->is_comptime == nullptr)
return false;
// When the is_comptime field references an instruction that has to get analyzed, this
// is the value.
if (var->is_comptime->child != nullptr) {
assert(var->is_comptime->child->value.type->id == ZigTypeIdBool);
return var->is_comptime->child->value.data.x_bool;
}
// As an optimization, is_comptime values which are constant are allowed
// to be omitted from analysis. In this case, there is no child instruction
// and we simply look at the unanalyzed const parent instruction.
assert(var->is_comptime->value.type->id == ZigTypeIdBool);
return var->is_comptime->value.data.x_bool;
}
bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
if (a->data.x_ptr.special != b->data.x_ptr.special)
return false;
if (a->data.x_ptr.mut != b->data.x_ptr.mut)
return false;
switch (a->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
return false;
return true;
case ConstPtrSpecialBaseArray:
if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
a->data.x_ptr.data.base_array.array_val->global_refs !=
b->data.x_ptr.data.base_array.array_val->global_refs)
{
return false;
}
if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
return false;
if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
return false;
return true;
case ConstPtrSpecialBaseStruct:
if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
a->data.x_ptr.data.base_struct.struct_val->global_refs !=
b->data.x_ptr.data.base_struct.struct_val->global_refs)
{
return false;
}
if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
return false;
return true;
case ConstPtrSpecialBaseErrorUnionCode:
if (a->data.x_ptr.data.base_err_union_code.err_union_val !=
b->data.x_ptr.data.base_err_union_code.err_union_val &&
a->data.x_ptr.data.base_err_union_code.err_union_val->global_refs !=
b->data.x_ptr.data.base_err_union_code.err_union_val->global_refs)
{
return false;
}
return true;
case ConstPtrSpecialBaseErrorUnionPayload:
if (a->data.x_ptr.data.base_err_union_payload.err_union_val !=
b->data.x_ptr.data.base_err_union_payload.err_union_val &&
a->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs !=
b->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs)
{
return false;
}
return true;
case ConstPtrSpecialBaseOptionalPayload:
if (a->data.x_ptr.data.base_optional_payload.optional_val !=
b->data.x_ptr.data.base_optional_payload.optional_val &&
a->data.x_ptr.data.base_optional_payload.optional_val->global_refs !=
b->data.x_ptr.data.base_optional_payload.optional_val->global_refs)
{
return false;
}
return true;
case ConstPtrSpecialHardCodedAddr:
if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
return false;
return true;
case ConstPtrSpecialDiscard:
return true;
case ConstPtrSpecialFunction:
return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
case ConstPtrSpecialNull:
return true;
}
zig_unreachable();
}
static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) {
assert(a->data.x_array.special != ConstArraySpecialUndef);
assert(b->data.x_array.special != ConstArraySpecialUndef);
if (a->data.x_array.special == ConstArraySpecialBuf &&
b->data.x_array.special == ConstArraySpecialBuf)
{
return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
}
expand_undef_array(g, a);
expand_undef_array(g, b);
ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
for (size_t i = 0; i < len; i += 1) {
if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
return false;
}
return true;
}
bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
assert(a->type->id == b->type->id);
assert(a->special == ConstValSpecialStatic);
assert(b->special == ConstValSpecialStatic);
switch (a->type->id) {
case ZigTypeIdOpaque:
zig_unreachable();
case ZigTypeIdEnum:
return bigint_cmp(&a->data.x_enum_tag, &b->data.x_enum_tag) == CmpEQ;
case ZigTypeIdUnion: {
ConstUnionValue *union1 = &a->data.x_union;
ConstUnionValue *union2 = &b->data.x_union;
if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) {
TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag);
assert(field != nullptr);
if (!type_has_bits(field->type_entry))
return true;
assert(find_union_field_by_tag(a->type, &union2->tag) != nullptr);
return const_values_equal(g, union1->payload, union2->payload);
}
return false;
}
case ZigTypeIdMetaType:
return a->data.x_type == b->data.x_type;
case ZigTypeIdVoid:
return true;
case ZigTypeIdErrorSet:
return a->data.x_err_set->value == b->data.x_err_set->value;
case ZigTypeIdBool:
return a->data.x_bool == b->data.x_bool;
case ZigTypeIdFloat:
assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count);
switch (a->type->data.floating.bit_count) {
case 16:
return f16_eq(a->data.x_f16, b->data.x_f16);
case 32:
return a->data.x_f32 == b->data.x_f32;
case 64:
return a->data.x_f64 == b->data.x_f64;
case 128:
return f128M_eq(&a->data.x_f128, &b->data.x_f128);
default:
zig_unreachable();
}
case ZigTypeIdComptimeFloat:
return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ;
case ZigTypeIdInt:
case ZigTypeIdComptimeInt:
return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
case ZigTypeIdEnumLiteral:
return buf_eql_buf(a->data.x_enum_literal, b->data.x_enum_literal);
case ZigTypeIdPointer:
case ZigTypeIdFn:
return const_values_equal_ptr(a, b);
case ZigTypeIdVector:
assert(a->type->data.vector.len == b->type->data.vector.len);
return const_values_equal_array(g, a, b, a->type->data.vector.len);
case ZigTypeIdArray: {
assert(a->type->data.array.len == b->type->data.array.len);
return const_values_equal_array(g, a, b, a->type->data.array.len);
}
case ZigTypeIdStruct:
for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
ConstExprValue *field_a = &a->data.x_struct.fields[i];
ConstExprValue *field_b = &b->data.x_struct.fields[i];
if (!const_values_equal(g, field_a, field_b))
return false;
}
return true;
case ZigTypeIdUndefined:
zig_panic("TODO");
case ZigTypeIdNull:
zig_panic("TODO");
case ZigTypeIdOptional:
if (get_codegen_ptr_type(a->type) != nullptr)
return const_values_equal_ptr(a, b);
if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) {
return (a->data.x_optional == nullptr && b->data.x_optional == nullptr);
} else {
return const_values_equal(g, a->data.x_optional, b->data.x_optional);
}
case ZigTypeIdErrorUnion:
zig_panic("TODO");
case ZigTypeIdArgTuple:
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
case ZigTypeIdPromise:
zig_unreachable();
}
zig_unreachable();
}
void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max) {
assert(int_type->id == ZigTypeIdInt);
if (int_type->data.integral.bit_count == 0) {
bigint_init_unsigned(bigint, 0);
return;
}
if (is_max) {
// is_signed=true (1 << (bit_count - 1)) - 1
// is_signed=false (1 << (bit_count - 0)) - 1
BigInt one = {0};
bigint_init_unsigned(&one, 1);
size_t shift_amt = int_type->data.integral.bit_count - (int_type->data.integral.is_signed ? 1 : 0);
BigInt bit_count_bi = {0};
bigint_init_unsigned(&bit_count_bi, shift_amt);
BigInt shifted_bi = {0};
bigint_shl(&shifted_bi, &one, &bit_count_bi);
bigint_sub(bigint, &shifted_bi, &one);
} else if (int_type->data.integral.is_signed) {
// - (1 << (bit_count - 1))
BigInt one = {0};
bigint_init_unsigned(&one, 1);
BigInt bit_count_bi = {0};
bigint_init_unsigned(&bit_count_bi, int_type->data.integral.bit_count - 1);
BigInt shifted_bi = {0};
bigint_shl(&shifted_bi, &one, &bit_count_bi);
bigint_negate(bigint, &shifted_bi);
} else {
bigint_init_unsigned(bigint, 0);
}
}
void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_val, bool is_max) {
if (type_entry->id == ZigTypeIdInt) {
const_val->special = ConstValSpecialStatic;
eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max);
} else if (type_entry->id == ZigTypeIdBool) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_bool = is_max;
} else if (type_entry->id == ZigTypeIdVoid) {
// nothing to do
} else {
zig_unreachable();
}
}
static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) {
assert(type_entry->id == ZigTypeIdPointer);
if (type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) {
buf_append_buf(buf, &type_entry->name);
return;
}
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
case ConstPtrSpecialBaseStruct:
case ConstPtrSpecialBaseErrorUnionCode:
case ConstPtrSpecialBaseErrorUnionPayload:
case ConstPtrSpecialBaseOptionalPayload:
buf_appendf(buf, "*");
// TODO we need a source node for const_ptr_pointee because it can generate compile errors
render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
return;
case ConstPtrSpecialBaseArray:
if (const_val->data.x_ptr.data.base_array.is_cstr) {
buf_appendf(buf, "*(c str lit)");
return;
} else {
buf_appendf(buf, "*");
// TODO we need a source node for const_ptr_pointee because it can generate compile errors
render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
return;
}
case ConstPtrSpecialHardCodedAddr:
buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name),
const_val->data.x_ptr.data.hard_coded_addr.addr);
return;
case ConstPtrSpecialDiscard:
buf_append_str(buf, "*_");
return;
case ConstPtrSpecialFunction:
{
ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
return;
}
case ConstPtrSpecialNull:
buf_append_str(buf, "null");
return;
}
zig_unreachable();
}
static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) {
if (const_val->data.x_err_set == nullptr) {
buf_append_str(buf, "null");
} else {
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name));
}
}
static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ConstExprValue *const_val, uint64_t start, uint64_t len) {
ConstArrayValue *array = &const_val->data.x_array;
switch (array->special) {
case ConstArraySpecialUndef:
buf_append_str(buf, "undefined");
return;
case ConstArraySpecialBuf: {
Buf *array_buf = array->data.s_buf;
const char *base = &buf_ptr(array_buf)[start];
assert(start + len <= buf_len(array_buf));
buf_append_char(buf, '"');
for (size_t i = 0; i < len; i += 1) {
uint8_t c = base[i];
if (c == '"') {
buf_append_str(buf, "\\\"");
} else {
buf_append_char(buf, c);
}
}
buf_append_char(buf, '"');
return;
}
case ConstArraySpecialNone: {
ConstExprValue *base = &array->data.s_none.elements[start];
assert(start + len <= const_val->type->data.array.len);
buf_appendf(buf, "%s{", buf_ptr(type_name));
for (uint64_t i = 0; i < len; i += 1) {
if (i != 0) buf_appendf(buf, ",");
render_const_value(g, buf, &base[i]);
}
buf_appendf(buf, "}");
return;
}
}
zig_unreachable();
}
void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
buf_appendf(buf, "(runtime value)");
return;
case ConstValSpecialUndef:
buf_appendf(buf, "undefined");
return;
case ConstValSpecialStatic:
break;
}
assert(const_val->type);
ZigType *type_entry = const_val->type;
switch (type_entry->id) {
case ZigTypeIdOpaque:
zig_unreachable();
case ZigTypeIdInvalid:
buf_appendf(buf, "(invalid)");
return;
case ZigTypeIdVoid:
buf_appendf(buf, "{}");
return;
case ZigTypeIdComptimeFloat:
bigfloat_append_buf(buf, &const_val->data.x_bigfloat);
return;
case ZigTypeIdFloat:
switch (type_entry->data.floating.bit_count) {
case 16:
buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16));
return;
case 32:
buf_appendf(buf, "%f", const_val->data.x_f32);
return;
case 64:
buf_appendf(buf, "%f", const_val->data.x_f64);
return;
case 128:
{
const size_t extra_len = 100;
size_t old_len = buf_len(buf);
buf_resize(buf, old_len + extra_len);
float64_t f64_value = f128M_to_f64(&const_val->data.x_f128);
double double_value;
memcpy(&double_value, &f64_value, sizeof(double));
// TODO actual f128 printing to decimal
int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value);
assert(len > 0);
buf_resize(buf, old_len + len);
return;
}
default:
zig_unreachable();
}
case ZigTypeIdComptimeInt:
case ZigTypeIdInt:
bigint_append_buf(buf, &const_val->data.x_bigint, 10);
return;
case ZigTypeIdEnumLiteral:
buf_append_buf(buf, const_val->data.x_enum_literal);
return;
case ZigTypeIdMetaType:
buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name));
return;
case ZigTypeIdUnreachable:
buf_appendf(buf, "unreachable");
return;
case ZigTypeIdBool:
{
const char *value = const_val->data.x_bool ? "true" : "false";
buf_appendf(buf, "%s", value);
return;
}
case ZigTypeIdFn:
{
assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name));
return;
}
case ZigTypeIdPointer:
return render_const_val_ptr(g, buf, const_val, type_entry);
case ZigTypeIdArray: {
uint64_t len = type_entry->data.array.len;
render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
return;
}
case ZigTypeIdVector: {
uint32_t len = type_entry->data.vector.len;
render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
return;
}
case ZigTypeIdNull:
{
buf_appendf(buf, "null");
return;
}
case ZigTypeIdUndefined:
{
buf_appendf(buf, "undefined");
return;
}
case ZigTypeIdOptional:
{
if (get_codegen_ptr_type(const_val->type) != nullptr)
return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet)
return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type);
if (const_val->data.x_optional) {
render_const_value(g, buf, const_val->data.x_optional);
} else {
buf_appendf(buf, "null");
}
return;
}
case ZigTypeIdBoundFn:
{
ZigFn *fn_entry = const_val->data.x_bound_fn.fn;
buf_appendf(buf, "(bound fn %s)", buf_ptr(&fn_entry->symbol_name));
return;
}
case ZigTypeIdStruct:
{
if (is_slice(type_entry)) {
ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index];
size_t len = bigint_as_unsigned(&len_val->data.x_bigint);
ConstExprValue *ptr_val = &const_val->data.x_struct.fields[slice_ptr_index];
if (ptr_val->special == ConstValSpecialUndef) {
assert(len == 0);
buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name));
return;
}
assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
ConstExprValue *array = ptr_val->data.x_ptr.data.base_array.array_val;
size_t start = ptr_val->data.x_ptr.data.base_array.elem_index;
render_const_val_array(g, buf, &type_entry->name, array, start, len);
} else {
buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name));
}
return;
}
case ZigTypeIdEnum:
{
TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag);
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));
return;
}
case ZigTypeIdErrorUnion:
{
buf_appendf(buf, "%s(", buf_ptr(&type_entry->name));
ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
if (err_set == nullptr) {
render_const_value(g, buf, const_val->data.x_err_union.payload);
} else {
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name),
buf_ptr(&err_set->name));
}
buf_appendf(buf, ")");
return;
}
case ZigTypeIdUnion:
{
const BigInt *tag = &const_val->data.x_union.tag;
TypeUnionField *field = find_union_field_by_tag(type_entry, tag);
buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name));
render_const_value(g, buf, const_val->data.x_union.payload);
buf_append_str(buf, "}");
return;
}
case ZigTypeIdErrorSet:
return render_const_val_err_set(g, buf, const_val, type_entry);
case ZigTypeIdArgTuple:
{
buf_appendf(buf, "(args value)");
return;
}
case ZigTypeIdPromise:
zig_unreachable();
}
zig_unreachable();
}
ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
assert(size_in_bits <= 65535);
ZigType *entry = new_type_table_entry(ZigTypeIdInt);
entry->size_in_bits = size_in_bits;
if (size_in_bits != 0) {
entry->llvm_type = LLVMIntType(size_in_bits);
entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type);
entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type);
}
const char u_or_i = is_signed ? 'i' : 'u';
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "%c%" PRIu32, u_or_i, size_in_bits);
entry->data.integral.is_signed = is_signed;
entry->data.integral.bit_count = size_in_bits;
return entry;
}
uint32_t type_id_hash(TypeId x) {
switch (x.id) {
case ZigTypeIdInvalid:
case ZigTypeIdOpaque:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdFloat:
case ZigTypeIdStruct:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
zig_unreachable();
case ZigTypeIdErrorUnion:
return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
case ZigTypeIdPointer:
return hash_ptr(x.data.pointer.child_type) +
((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
(x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
(x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
(((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
(((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) +
(((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881);
case ZigTypeIdArray:
return hash_ptr(x.data.array.child_type) +
((uint32_t)x.data.array.size ^ (uint32_t)2122979968);
case ZigTypeIdInt:
return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) +
(((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557);
case ZigTypeIdVector:
return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681);
}
zig_unreachable();
}
bool type_id_eql(TypeId a, TypeId b) {
if (a.id != b.id)
return false;
switch (a.id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdFloat:
case ZigTypeIdStruct:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdOptional:
case ZigTypeIdPromise:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
zig_unreachable();
case ZigTypeIdErrorUnion:
return a.data.error_union.err_set_type == b.data.error_union.err_set_type &&
a.data.error_union.payload_type == b.data.error_union.payload_type;
case ZigTypeIdPointer:
return a.data.pointer.child_type == b.data.pointer.child_type &&
a.data.pointer.ptr_len == b.data.pointer.ptr_len &&
a.data.pointer.is_const == b.data.pointer.is_const &&
a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
a.data.pointer.allow_zero == b.data.pointer.allow_zero &&
a.data.pointer.alignment == b.data.pointer.alignment &&
a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes;
case ZigTypeIdArray:
return a.data.array.child_type == b.data.array.child_type &&
a.data.array.size == b.data.array.size;
case ZigTypeIdInt:
return a.data.integer.is_signed == b.data.integer.is_signed &&
a.data.integer.bit_count == b.data.integer.bit_count;
case ZigTypeIdVector:
return a.data.vector.elem_type == b.data.vector.elem_type &&
a.data.vector.len == b.data.vector.len;
}
zig_unreachable();
}
uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
switch (x.id) {
case ZigLLVMFnIdCtz:
return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;
case ZigLLVMFnIdClz:
return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
case ZigLLVMFnIdPopCount:
return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049;
case ZigLLVMFnIdFloor:
return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
case ZigLLVMFnIdCeil:
return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089;
case ZigLLVMFnIdSqrt:
return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385;
case ZigLLVMFnIdBswap:
return (uint32_t)(x.data.bswap.bit_count) * (uint32_t)3661994335;
case ZigLLVMFnIdBitReverse:
return (uint32_t)(x.data.bit_reverse.bit_count) * (uint32_t)2621398431;
case ZigLLVMFnIdOverflowArithmetic:
return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) +
x.data.overflow_arithmetic.vector_len * 1435156945;
}
zig_unreachable();
}
bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
if (a.id != b.id)
return false;
switch (a.id) {
case ZigLLVMFnIdCtz:
return a.data.ctz.bit_count == b.data.ctz.bit_count;
case ZigLLVMFnIdClz:
return a.data.clz.bit_count == b.data.clz.bit_count;
case ZigLLVMFnIdPopCount:
return a.data.pop_count.bit_count == b.data.pop_count.bit_count;
case ZigLLVMFnIdBswap:
return a.data.bswap.bit_count == b.data.bswap.bit_count;
case ZigLLVMFnIdBitReverse:
return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count;
case ZigLLVMFnIdFloor:
case ZigLLVMFnIdCeil:
case ZigLLVMFnIdSqrt:
return a.data.floating.bit_count == b.data.floating.bit_count;
case ZigLLVMFnIdOverflowArithmetic:
return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
(a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
(a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed) &&
(a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len);
}
zig_unreachable();
}
// Canonicalize the array value as ConstArraySpecialNone
void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
size_t elem_count;
ZigType *elem_type;
if (const_val->type->id == ZigTypeIdArray) {
elem_count = const_val->type->data.array.len;
elem_type = const_val->type->data.array.child_type;
} else if (const_val->type->id == ZigTypeIdVector) {
elem_count = const_val->type->data.vector.len;
elem_type = const_val->type->data.vector.elem_type;
} else {
zig_unreachable();
}
if (const_val->special == ConstValSpecialUndef) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_array.special = ConstArraySpecialUndef;
}
switch (const_val->data.x_array.special) {
case ConstArraySpecialNone:
return;
case ConstArraySpecialUndef: {
const_val->data.x_array.special = ConstArraySpecialNone;
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
element_val->type = elem_type;
init_const_undefined(g, element_val);
element_val->parent.id = ConstParentIdArray;
element_val->parent.data.p_array.array_val = const_val;
element_val->parent.data.p_array.elem_index = i;
}
return;
}
case ConstArraySpecialBuf: {
Buf *buf = const_val->data.x_array.data.s_buf;
// If we're doing this it means that we are potentially modifying the data,
// so we can't have it be in the string literals table
g->string_literals_table.maybe_remove(buf);
const_val->data.x_array.special = ConstArraySpecialNone;
assert(elem_count == buf_len(buf));
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *this_char = &const_val->data.x_array.data.s_none.elements[i];
this_char->special = ConstValSpecialStatic;
this_char->type = g->builtin_types.entry_u8;
bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
this_char->parent.id = ConstParentIdArray;
this_char->parent.data.p_array.array_val = const_val;
this_char->parent.data.p_array.elem_index = i;
}
return;
}
}
zig_unreachable();
}
// Deprecated. Reference the parent field directly.
ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
return &value->parent;
}
static const ZigTypeId all_type_ids[] = {
ZigTypeIdMetaType,
ZigTypeIdVoid,
ZigTypeIdBool,
ZigTypeIdUnreachable,
ZigTypeIdInt,
ZigTypeIdFloat,
ZigTypeIdPointer,
ZigTypeIdArray,
ZigTypeIdStruct,
ZigTypeIdComptimeFloat,
ZigTypeIdComptimeInt,
ZigTypeIdUndefined,
ZigTypeIdNull,
ZigTypeIdOptional,
ZigTypeIdErrorUnion,
ZigTypeIdErrorSet,
ZigTypeIdEnum,
ZigTypeIdUnion,
ZigTypeIdFn,
ZigTypeIdBoundFn,
ZigTypeIdArgTuple,
ZigTypeIdOpaque,
ZigTypeIdPromise,
ZigTypeIdVector,
ZigTypeIdEnumLiteral,
};
ZigTypeId type_id_at_index(size_t index) {
assert(index < array_length(all_type_ids));
return all_type_ids[index];
}
size_t type_id_len() {
return array_length(all_type_ids);
}
size_t type_id_index(ZigType *entry) {
switch (entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
return 0;
case ZigTypeIdVoid:
return 1;
case ZigTypeIdBool:
return 2;
case ZigTypeIdUnreachable:
return 3;
case ZigTypeIdInt:
return 4;
case ZigTypeIdFloat:
return 5;
case ZigTypeIdPointer:
return 6;
case ZigTypeIdArray:
return 7;
case ZigTypeIdStruct:
if (entry->data.structure.is_slice)
return 6;
return 8;
case ZigTypeIdComptimeFloat:
return 9;
case ZigTypeIdComptimeInt:
return 10;
case ZigTypeIdUndefined:
return 11;
case ZigTypeIdNull:
return 12;
case ZigTypeIdOptional:
return 13;
case ZigTypeIdErrorUnion:
return 14;
case ZigTypeIdErrorSet:
return 15;
case ZigTypeIdEnum:
return 16;
case ZigTypeIdUnion:
return 17;
case ZigTypeIdFn:
return 18;
case ZigTypeIdBoundFn:
return 19;
case ZigTypeIdArgTuple:
return 20;
case ZigTypeIdOpaque:
return 21;
case ZigTypeIdPromise:
return 22;
case ZigTypeIdVector:
return 23;
case ZigTypeIdEnumLiteral:
return 24;
}
zig_unreachable();
}
const char *type_id_name(ZigTypeId id) {
switch (id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
return "Type";
case ZigTypeIdVoid:
return "Void";
case ZigTypeIdBool:
return "Bool";
case ZigTypeIdUnreachable:
return "NoReturn";
case ZigTypeIdInt:
return "Int";
case ZigTypeIdFloat:
return "Float";
case ZigTypeIdPointer:
return "Pointer";
case ZigTypeIdArray:
return "Array";
case ZigTypeIdStruct:
return "Struct";
case ZigTypeIdComptimeFloat:
return "ComptimeFloat";
case ZigTypeIdComptimeInt:
return "ComptimeInt";
case ZigTypeIdEnumLiteral:
return "EnumLiteral";
case ZigTypeIdUndefined:
return "Undefined";
case ZigTypeIdNull:
return "Null";
case ZigTypeIdOptional:
return "Optional";
case ZigTypeIdErrorUnion:
return "ErrorUnion";
case ZigTypeIdErrorSet:
return "ErrorSet";
case ZigTypeIdEnum:
return "Enum";
case ZigTypeIdUnion:
return "Union";
case ZigTypeIdFn:
return "Fn";
case ZigTypeIdBoundFn:
return "BoundFn";
case ZigTypeIdArgTuple:
return "ArgTuple";
case ZigTypeIdOpaque:
return "Opaque";
case ZigTypeIdPromise:
return "Promise";
case ZigTypeIdVector:
return "Vector";
}
zig_unreachable();
}
LinkLib *create_link_lib(Buf *name) {
LinkLib *link_lib = allocate<LinkLib>(1);
link_lib->name = name;
return link_lib;
}
LinkLib *add_link_lib(CodeGen *g, Buf *name) {
bool is_libc = buf_eql_str(name, "c");
if (is_libc && g->libc_link_lib != nullptr)
return g->libc_link_lib;
for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
LinkLib *existing_lib = g->link_libs_list.at(i);
if (buf_eql_buf(existing_lib->name, name)) {
return existing_lib;
}
}
LinkLib *link_lib = create_link_lib(name);
g->link_libs_list.append(link_lib);
if (is_libc)
g->libc_link_lib = link_lib;
return link_lib;
}
ZigType *get_align_amt_type(CodeGen *g) {
if (g->align_amt_type == nullptr) {
// according to LLVM the maximum alignment is 1 << 29.
g->align_amt_type = get_int_type(g, false, 29);
}
return g->align_amt_type;
}
uint32_t type_ptr_hash(const ZigType *ptr) {
return hash_ptr((void*)ptr);
}
bool type_ptr_eql(const ZigType *a, const ZigType *b) {
return a == b;
}
ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name));
resolve_top_level_decl(codegen, tld, nullptr);
assert(tld->id == TldIdVar);
TldVar *tld_var = (TldVar *)tld;
ConstExprValue *var_value = tld_var->var->const_value;
assert(var_value != nullptr);
return var_value;
}
bool type_is_global_error_set(ZigType *err_set_type) {
assert(err_set_type->id == ZigTypeIdErrorSet);
assert(err_set_type->data.error_set.infer_fn == nullptr);
return err_set_type->data.error_set.err_count == UINT32_MAX;
}
uint32_t get_coro_frame_align_bytes(CodeGen *g) {
uint32_t a = g->pointer_size_bytes * 2;
// promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
if (a < 8) a = 8;
return a;
}
bool type_can_fail(ZigType *type_entry) {
return type_entry->id == ZigTypeIdErrorUnion || type_entry->id == ZigTypeIdErrorSet;
}
bool fn_type_can_fail(FnTypeId *fn_type_id) {
return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;
}
// ErrorNone - result pointer has the type
// ErrorOverflow - an integer primitive type has too large a bit width
// ErrorPrimitiveTypeNotFound - result pointer unchanged
Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result) {
if (buf_len(name) >= 2) {
uint8_t first_c = buf_ptr(name)[0];
if (first_c == 'i' || first_c == 'u') {
for (size_t i = 1; i < buf_len(name); i += 1) {
uint8_t c = buf_ptr(name)[i];
if (c < '0' || c > '9') {
goto not_integer;
}
}
bool is_signed = (first_c == 'i');
unsigned long int bit_count = strtoul(buf_ptr(name) + 1, nullptr, 10);
// strtoul returns ULONG_MAX on errors, so this comparison catches that as well.
if (bit_count >= 65536) return ErrorOverflow;
*result = get_int_type(g, is_signed, bit_count);
return ErrorNone;
}
}
not_integer:
auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
if (primitive_table_entry == nullptr)
return ErrorPrimitiveTypeNotFound;
*result = primitive_table_entry->value;
return ErrorNone;
}
Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents) {
if (g->enable_cache) {
return cache_add_file_fetch(&g->cache_hash, resolved_path, contents);
} else {
return os_fetch_file_path(resolved_path, contents);
}
}
static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
// https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
switch (ty->id) {
case ZigTypeIdEnum:
case ZigTypeIdInt:
case ZigTypeIdBool:
return X64CABIClass_INTEGER;
case ZigTypeIdFloat:
case ZigTypeIdVector:
return X64CABIClass_SSE;
case ZigTypeIdStruct:
case ZigTypeIdUnion: {
if (ty_size <= 8)
return X64CABIClass_INTEGER;
return X64CABIClass_MEMORY;
}
default:
return X64CABIClass_Unknown;
}
}
static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
switch (ty->id) {
case ZigTypeIdEnum:
case ZigTypeIdInt:
case ZigTypeIdBool:
return X64CABIClass_INTEGER;
case ZigTypeIdFloat:
case ZigTypeIdVector:
return X64CABIClass_SSE;
case ZigTypeIdStruct: {
// "If the size of an object is larger than four eightbytes, or it contains unaligned
// fields, it has class MEMORY"
if (ty_size > 32)
return X64CABIClass_MEMORY;
if (ty->data.structure.layout != ContainerLayoutExtern) {
// TODO determine whether packed structs have any unaligned fields
return X64CABIClass_Unknown;
}
// "If the size of the aggregate exceeds two eightbytes and the first eight-
// byte isnt SSE or any other eightbyte isnt SSEUP, the whole argument
// is passed in memory."
if (ty_size > 16) {
// Zig doesn't support vectors and large fp registers yet, so this will always
// be memory.
return X64CABIClass_MEMORY;
}
X64CABIClass working_class = X64CABIClass_Unknown;
for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry);
if (field_class == X64CABIClass_Unknown)
return X64CABIClass_Unknown;
if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
working_class = field_class;
}
}
return working_class;
}
case ZigTypeIdUnion: {
// "If the size of an object is larger than four eightbytes, or it contains unaligned
// fields, it has class MEMORY"
if (ty_size > 32)
return X64CABIClass_MEMORY;
if (ty->data.unionation.layout != ContainerLayoutExtern)
return X64CABIClass_MEMORY;
// "If the size of the aggregate exceeds two eightbytes and the first eight-
// byte isnt SSE or any other eightbyte isnt SSEUP, the whole argument
// is passed in memory."
if (ty_size > 16) {
// Zig doesn't support vectors and large fp registers yet, so this will always
// be memory.
return X64CABIClass_MEMORY;
}
X64CABIClass working_class = X64CABIClass_Unknown;
for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) {
X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
if (field_class == X64CABIClass_Unknown)
return X64CABIClass_Unknown;
if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
working_class = field_class;
}
}
return working_class;
}
default:
return X64CABIClass_Unknown;
}
}
X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
const size_t ty_size = type_size(g, ty);
if (get_codegen_ptr_type(ty) != nullptr)
return X64CABIClass_INTEGER;
if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) {
return type_windows_abi_x86_64_class(g, ty, ty_size);
} else {
return type_system_V_abi_x86_64_class(g, ty, ty_size);
}
}
// NOTE this does not depend on x86_64
bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
return (ty->id == ZigTypeIdInt ||
ty->id == ZigTypeIdFloat ||
ty->id == ZigTypeIdBool ||
ty->id == ZigTypeIdEnum ||
ty->id == ZigTypeIdVoid ||
ty->id == ZigTypeIdUnreachable ||
get_codegen_ptr_type(ty) != nullptr);
}
uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) {
assert(struct_type->id == ZigTypeIdStruct);
assert(type_is_resolved(struct_type, ResolveStatusSizeKnown));
if (struct_type->data.structure.host_int_bytes == nullptr)
return 0;
return struct_type->data.structure.host_int_bytes[field->gen_index];
}
Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *const_val, ZigType *wanted_type)
{
ConstExprValue ptr_val = {};
ptr_val.special = ConstValSpecialStatic;
ptr_val.type = get_pointer_to_type(codegen, wanted_type, true);
ptr_val.data.x_ptr.mut = ConstPtrMutComptimeConst;
ptr_val.data.x_ptr.special = ConstPtrSpecialRef;
ptr_val.data.x_ptr.data.ref.pointee = const_val;
if (const_ptr_pointee(ira, codegen, &ptr_val, source_node) == nullptr)
return ErrorSemanticAnalyzeFail;
return ErrorNone;
}
const char *container_string(ContainerKind kind) {
switch (kind) {
case ContainerKindEnum: return "enum";
case ContainerKindStruct: return "struct";
case ContainerKindUnion: return "union";
}
zig_unreachable();
}
bool ptr_allows_addr_zero(ZigType *ptr_type) {
if (ptr_type->id == ZigTypeIdPointer) {
return ptr_type->data.pointer.allow_zero;
} else if (ptr_type->id == ZigTypeIdOptional) {
return true;
}
return false;
}
void emit_error_notes_for_ref_stack(CodeGen *g, ErrorMsg *msg) {
size_t i = g->tld_ref_source_node_stack.length;
for (;;) {
if (i == 0)
break;
i -= 1;
AstNode *source_node = g->tld_ref_source_node_stack.at(i);
if (source_node) {
msg = add_error_note(g, msg, source_node, buf_sprintf("referenced here"));
}
}
}
Buf *type_bare_name(ZigType *type_entry) {
if (is_slice(type_entry)) {
return &type_entry->name;
} else if (is_container(type_entry)) {
return get_container_scope(type_entry)->bare_name;
} else if (type_entry->id == ZigTypeIdOpaque) {
return type_entry->data.opaque.bare_name;
} else {
return &type_entry->name;
}
}
// TODO this will have to be more clever, probably using the full name
// and replacing '.' with '_' or something like that
Buf *type_h_name(ZigType *t) {
return type_bare_name(t);
}
static void resolve_llvm_types_slice(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
if (type->data.structure.resolve_status >= wanted_resolve_status) return;
ZigType *ptr_type = type->data.structure.fields[slice_ptr_index].type_entry;
ZigType *child_type = ptr_type->data.pointer.child_type;
ZigType *usize_type = g->builtin_types.entry_usize;
bool done = false;
if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero)
{
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenUnknown, 0, 0, 0, false);
ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status));
type->llvm_type = peer_slice_type->llvm_type;
type->llvm_di_type = peer_slice_type->llvm_di_type;
type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status;
done = true;
}
// If the child type is []const T then we need to make sure the type ref
// and debug info is the same as if the child type were []T.
if (is_slice(child_type)) {
ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(child_ptr_type->id == ZigTypeIdPointer);
if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero)
{
ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
PtrLenUnknown, 0, 0, 0, false);
ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type);
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false,
PtrLenUnknown, 0, 0, 0, false);
ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status));
type->llvm_type = peer_slice_type->llvm_type;
type->llvm_di_type = peer_slice_type->llvm_di_type;
type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status;
done = true;
}
}
if (done) return;
LLVMTypeRef usize_llvm_type = get_llvm_type(g, usize_type);
ZigLLVMDIType *usize_llvm_di_type = get_llvm_di_type(g, usize_type);
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
if (type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) {
type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name));
type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
compile_unit_scope, di_file, line);
type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl;
if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
}
if (!type_has_bits(child_type)) {
LLVMTypeRef element_types[] = {
usize_llvm_type,
};
LLVMStructSetBody(type->llvm_type, element_types, 1, false);
uint64_t len_debug_size_in_bits = usize_type->size_in_bits;
uint64_t len_debug_align_in_bits = 8*usize_type->abi_align;
uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
uint64_t debug_size_in_bits = type->size_in_bits;
uint64_t debug_align_in_bits = 8*type->abi_align;
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"len", di_file, line,
len_debug_size_in_bits,
len_debug_align_in_bits,
len_offset_in_bits,
0, usize_llvm_di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&type->name),
di_file, line, debug_size_in_bits, debug_align_in_bits, 0,
nullptr, di_element_types, 1, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
type->llvm_di_type = replacement_di_type;
type->data.structure.resolve_status = ResolveStatusLLVMFull;
return;
}
LLVMTypeRef element_types[2];
element_types[slice_ptr_index] = get_llvm_type(g, ptr_type);
element_types[slice_len_index] = get_llvm_type(g, g->builtin_types.entry_usize);
if (type->data.structure.resolve_status >= wanted_resolve_status) return;
LLVMStructSetBody(type->llvm_type, element_types, 2, false);
uint64_t ptr_debug_size_in_bits = ptr_type->size_in_bits;
uint64_t ptr_debug_align_in_bits = 8*ptr_type->abi_align;
uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
uint64_t len_debug_size_in_bits = usize_type->size_in_bits;
uint64_t len_debug_align_in_bits = 8*usize_type->abi_align;
uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1);
uint64_t debug_size_in_bits = type->size_in_bits;
uint64_t debug_align_in_bits = 8*type->abi_align;
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"ptr", di_file, line,
ptr_debug_size_in_bits,
ptr_debug_align_in_bits,
ptr_offset_in_bits,
0, get_llvm_di_type(g, ptr_type)),
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"len", di_file, line,
len_debug_size_in_bits,
len_debug_align_in_bits,
len_offset_in_bits,
0, usize_llvm_di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&type->name),
di_file, line, debug_size_in_bits, debug_align_in_bits, 0,
nullptr, di_element_types, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
type->llvm_di_type = replacement_di_type;
type->data.structure.resolve_status = ResolveStatusLLVMFull;
}
static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveStatus wanted_resolve_status) {
assert(struct_type->id == ZigTypeIdStruct);
assert(struct_type->data.structure.resolve_status != ResolveStatusInvalid);
assert(struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown);
assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return;
AstNode *decl_node = struct_type->data.structure.decl_node;
ZigLLVMDIFile *di_file;
ZigLLVMDIScope *di_scope;
unsigned line;
if (decl_node != nullptr) {
assert(decl_node->type == NodeTypeContainerDecl);
Scope *scope = &struct_type->data.structure.decls_scope->base;
ZigType *import = get_scope_import(scope);
di_file = import->data.structure.root_struct->di_file;
di_scope = ZigLLVMFileToScope(di_file);
line = decl_node->line + 1;
} else {
di_file = nullptr;
di_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
line = 0;
}
if (struct_type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) {
struct_type->llvm_type = type_has_bits(struct_type) ?
LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&struct_type->name)) : LLVMVoidType();
unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
struct_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
dwarf_kind, buf_ptr(&struct_type->name),
di_scope, di_file, line);
struct_type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl;
if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
}
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);
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;
// trigger all the recursive get_llvm_type calls
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
ZigType *field_type = type_struct_field->type_entry;
if (!type_has_bits(field_type))
continue;
(void)get_llvm_type(g, field_type);
if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return;
}
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
ZigType *field_type = type_struct_field->type_entry;
if (!type_has_bits(field_type))
continue;
if (packed) {
size_t field_size_in_bits = type_size_bits(g, field_type);
size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
if (first_packed_bits_offset_misalign != SIZE_MAX) {
// this field is not byte-aligned; it is part of the previous field with a bit offset
size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign;
size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
if (full_abi_size * 8 == full_bit_count) {
// next field recovers ABI alignment
element_types[gen_field_index] = LLVMIntType((unsigned)(full_bit_count));
gen_field_index += 1;
first_packed_bits_offset_misalign = SIZE_MAX;
}
} else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) {
first_packed_bits_offset_misalign = packed_bits_offset;
} else {
// This is a byte-aligned field (both start and end) in a packed struct.
element_types[gen_field_index] = get_llvm_type(g, field_type);
gen_field_index += 1;
}
packed_bits_offset = next_packed_bits_offset;
} else {
element_types[gen_field_index] = get_llvm_type(g, field_type);
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;
size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
element_types[gen_field_index] = LLVMIntType((unsigned)full_abi_size * 8);
gen_field_index += 1;
}
if (type_has_bits(struct_type)) {
LLVMStructSetBody(struct_type->llvm_type, element_types, (unsigned)gen_field_count, packed);
}
ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(debug_field_count);
size_t debug_field_index = 0;
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *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;
}
ZigType *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 == ZigTypeIdFn) {
ZigType *field_ptr_type = get_pointer_to_type(g, field_type, true);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type));
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type));
field_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, field_type),
debug_size_in_bits, debug_align_in_bits, buf_ptr(&field_ptr_type->name));
} else {
field_di_type = get_llvm_di_type(g, field_type);
}
uint64_t debug_size_in_bits;
uint64_t debug_align_in_bits;
uint64_t debug_offset_in_bits;
if (packed) {
debug_size_in_bits = type_struct_field->type_entry->size_in_bits;
debug_align_in_bits = 8 * type_struct_field->type_entry->abi_align;
debug_offset_in_bits = 8 * type_struct_field->offset + type_struct_field->bit_offset_in_host;
} else {
debug_size_in_bits = 8 * get_store_size_bytes(field_type->size_in_bits);
debug_align_in_bits = 8 * field_type->abi_align;
debug_offset_in_bits = 8 * type_struct_field->offset;
}
unsigned line;
if (decl_node != nullptr) {
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
line = field_node->line + 1;
} else {
line = 0;
}
di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(struct_type->llvm_di_type), buf_ptr(type_struct_field->name),
di_file, line,
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*get_store_size_bytes(struct_type->size_in_bits);
uint64_t debug_align_in_bits = 8*struct_type->abi_align;
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
di_scope,
buf_ptr(&struct_type->name),
di_file, line,
debug_size_in_bits,
debug_align_in_bits,
0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, struct_type->llvm_di_type, replacement_di_type);
struct_type->llvm_di_type = replacement_di_type;
struct_type->data.structure.resolve_status = ResolveStatusLLVMFull;
}
static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type) {
assert(!enum_type->data.enumeration.is_invalid);
assert(enum_type->data.enumeration.complete);
if (enum_type->llvm_di_type != nullptr) return;
Scope *scope = &enum_type->data.enumeration.decls_scope->base;
ZigType *import = get_scope_import(scope);
AstNode *decl_node = enum_type->data.enumeration.decl_node;
if (!type_has_bits(enum_type)) {
enum_type->llvm_type = g->builtin_types.entry_void->llvm_type;
uint64_t debug_size_in_bits = 0;
uint64_t debug_align_in_bits = 0;
ZigLLVMDIType **di_element_types = nullptr;
size_t debug_field_count = 0;
enum_type->llvm_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
buf_ptr(&enum_type->name),
import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
debug_size_in_bits,
debug_align_in_bits,
0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
return;
}
uint32_t field_count = enum_type->data.enumeration.src_field_count;
assert(enum_type->data.enumeration.fields);
ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
for (uint32_t i = 0; i < field_count; i += 1) {
TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i];
// TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t
// http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html
di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name),
bigint_as_signed(&enum_field->value));
}
ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type;
enum_type->llvm_type = get_llvm_type(g, tag_int_type);
// create debug type for tag
uint64_t tag_debug_size_in_bits = tag_int_type->size_in_bits;
uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align;
ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name),
import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
tag_debug_size_in_bits,
tag_debug_align_in_bits,
di_enumerators, field_count,
get_llvm_di_type(g, tag_int_type), "");
enum_type->llvm_di_type = tag_di_type;
}
static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveStatus wanted_resolve_status) {
if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return;
ZigType *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;
ZigType *tag_type = union_type->data.unionation.tag_type;
if (most_aligned_union_member == nullptr) {
union_type->llvm_type = get_llvm_type(g, tag_type);
union_type->llvm_di_type = get_llvm_di_type(g, tag_type);
union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
return;
}
Scope *scope = &union_type->data.unionation.decls_scope->base;
ZigType *import = get_scope_import(scope);
AstNode *decl_node = union_type->data.unionation.decl_node;
if (union_type->data.unionation.resolve_status < ResolveStatusLLVMFwdDecl) {
union_type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&union_type->name));
size_t line = decl_node ? decl_node->line : 0;
unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
union_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
dwarf_kind, buf_ptr(&union_type->name),
ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
import->data.structure.root_struct->di_file, (unsigned)(line + 1));
union_type->data.unionation.resolve_status = ResolveStatusLLVMFwdDecl;
if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
}
uint32_t gen_field_count = union_type->data.unionation.gen_field_count;
ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(gen_field_count);
uint32_t field_count = union_type->data.unionation.src_field_count;
for (uint32_t i = 0; i < field_count; i += 1) {
TypeUnionField *union_field = &union_type->data.unionation.fields[i];
if (!type_has_bits(union_field->type_entry))
continue;
ZigLLVMDIType *field_di_type = get_llvm_di_type(g, union_field->type_entry);
if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return;
uint64_t store_size_in_bits = union_field->type_entry->size_in_bits;
uint64_t abi_align_in_bits = 8*union_field->type_entry->abi_align;
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name),
import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1),
store_size_in_bits,
abi_align_in_bits,
0,
0, field_di_type);
}
if (tag_type == nullptr || !type_has_bits(tag_type)) {
assert(most_aligned_union_member != nullptr);
size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->abi_size;
if (padding_bytes > 0) {
ZigType *u8_type = get_int_type(g, false, 8);
ZigType *padding_array = get_array_type(g, u8_type, padding_bytes);
LLVMTypeRef union_element_types[] = {
most_aligned_union_member->llvm_type,
get_llvm_type(g, padding_array),
};
LLVMStructSetBody(union_type->llvm_type, union_element_types, 2, false);
} else {
LLVMStructSetBody(union_type->llvm_type, &most_aligned_union_member->llvm_type, 1, false);
}
union_type->data.unionation.union_llvm_type = union_type->llvm_type;
union_type->data.unionation.gen_tag_index = SIZE_MAX;
union_type->data.unionation.gen_union_index = SIZE_MAX;
// create debug type for union
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&union_type->name),
import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
union_type->data.unionation.union_abi_size * 8,
most_aligned_union_member->abi_align * 8,
0, union_inner_di_types,
gen_field_count, 0, "");
ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type);
union_type->llvm_di_type = replacement_di_type;
union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
return;
}
LLVMTypeRef union_type_ref;
size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->abi_size;
if (padding_bytes == 0) {
union_type_ref = get_llvm_type(g, most_aligned_union_member);
} else {
ZigType *u8_type = get_int_type(g, false, 8);
ZigType *padding_array = get_array_type(g, u8_type, padding_bytes);
LLVMTypeRef union_element_types[] = {
get_llvm_type(g, most_aligned_union_member),
get_llvm_type(g, padding_array),
};
union_type_ref = LLVMStructType(union_element_types, 2, false);
}
union_type->data.unionation.union_llvm_type = union_type_ref;
LLVMTypeRef root_struct_element_types[2];
root_struct_element_types[union_type->data.unionation.gen_tag_index] = get_llvm_type(g, tag_type);
root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref;
LLVMStructSetBody(union_type->llvm_type, root_struct_element_types, 2, false);
// create debug type for union
ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
ZigLLVMTypeToScope(union_type->llvm_di_type), "AnonUnion",
import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
most_aligned_union_member->size_in_bits, 8*most_aligned_union_member->abi_align,
0, union_inner_di_types, gen_field_count, 0, "");
uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type,
union_type->data.unionation.gen_union_index);
uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type,
union_type->data.unionation.gen_tag_index);
ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(union_type->llvm_di_type), "payload",
import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
most_aligned_union_member->size_in_bits,
8*most_aligned_union_member->abi_align,
union_offset_in_bits,
0, union_di_type);
uint64_t tag_debug_size_in_bits = tag_type->size_in_bits;
uint64_t tag_debug_align_in_bits = 8*tag_type->abi_align;
ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(union_type->llvm_di_type), "tag",
import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
tag_debug_size_in_bits,
tag_debug_align_in_bits,
tag_offset_in_bits,
0, get_llvm_di_type(g, tag_type));
ZigLLVMDIType *di_root_members[2];
di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type;
di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type;
uint64_t debug_size_in_bits = union_type->size_in_bits;
uint64_t debug_align_in_bits = 8*union_type->abi_align;
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
buf_ptr(&union_type->name),
import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
debug_size_in_bits,
debug_align_in_bits,
0, nullptr, di_root_members, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type);
union_type->llvm_di_type = replacement_di_type;
union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
}
static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type) {
if (type->llvm_di_type != nullptr) return;
if (!type_has_bits(type)) {
type->llvm_type = g->builtin_types.entry_void->llvm_type;
type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
return;
}
ZigType *elem_type = type->data.pointer.child_type;
if (type->data.pointer.is_const || type->data.pointer.is_volatile ||
type->data.pointer.explicit_alignment != 0 || type->data.pointer.ptr_len != PtrLenSingle ||
type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero)
{
ZigType *peer_type = get_pointer_to_type_extra(g, elem_type, false, false,
PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false);
type->llvm_type = get_llvm_type(g, peer_type);
type->llvm_di_type = get_llvm_di_type(g, peer_type);
return;
}
if (type->data.pointer.host_int_bytes == 0) {
assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl));
type->llvm_type = LLVMPointerType(elem_type->llvm_type, 0);
uint64_t debug_size_in_bits = 8*get_store_size_bytes(type->size_in_bits);
uint64_t debug_align_in_bits = 8*type->abi_align;
type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, elem_type->llvm_di_type,
debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name));
assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFull));
} else {
ZigType *host_int_type = get_int_type(g, false, type->data.pointer.host_int_bytes * 8);
LLVMTypeRef host_int_llvm_type = get_llvm_type(g, host_int_type);
type->llvm_type = LLVMPointerType(host_int_llvm_type, 0);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, host_int_llvm_type);
uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, host_int_llvm_type);
type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, host_int_type),
debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name));
}
}
static void resolve_llvm_types_integer(CodeGen *g, ZigType *type) {
if (type->llvm_di_type != nullptr) return;
if (!type_has_bits(type)) {
type->llvm_type = g->builtin_types.entry_void->llvm_type;
type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
return;
}
unsigned dwarf_tag;
if (type->data.integral.is_signed) {
if (type->size_in_bits == 8) {
dwarf_tag = ZigLLVMEncoding_DW_ATE_signed_char();
} else {
dwarf_tag = ZigLLVMEncoding_DW_ATE_signed();
}
} else {
if (type->size_in_bits == 8) {
dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned_char();
} else {
dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned();
}
}
type->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&type->name), type->size_in_bits, dwarf_tag);
type->llvm_type = LLVMIntType(type->size_in_bits);
}
static void resolve_llvm_types_optional(CodeGen *g, ZigType *type) {
if (type->llvm_di_type != nullptr) return;
LLVMTypeRef bool_llvm_type = get_llvm_type(g, g->builtin_types.entry_bool);
ZigLLVMDIType *bool_llvm_di_type = get_llvm_di_type(g, g->builtin_types.entry_bool);
ZigType *child_type = type->data.maybe.child_type;
if (!type_has_bits(child_type)) {
type->llvm_type = bool_llvm_type;
type->llvm_di_type = bool_llvm_di_type;
return;
}
LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type);
ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type);
if (type_is_nonnull_ptr(child_type) || child_type->id == ZigTypeIdErrorSet) {
type->llvm_type = child_llvm_type;
type->llvm_di_type = child_llvm_di_type;
return;
}
LLVMTypeRef elem_types[] = {
get_llvm_type(g, child_type),
LLVMInt1Type(),
};
type->llvm_type = LLVMStructType(elem_types, 2, false);
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
compile_unit_scope, di_file, line);
uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_llvm_type);
uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_llvm_type);
uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_llvm_type);
uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_llvm_type);
uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"val", di_file, line,
val_debug_size_in_bits,
val_debug_align_in_bits,
val_offset_in_bits,
0, child_llvm_di_type),
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"maybe", di_file, line,
maybe_debug_size_in_bits,
maybe_debug_align_in_bits,
maybe_offset_in_bits,
0, bool_llvm_di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&type->name),
di_file, line, debug_size_in_bits, debug_align_in_bits, 0,
nullptr, di_element_types, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
type->llvm_di_type = replacement_di_type;
}
static void resolve_llvm_types_error_union(CodeGen *g, ZigType *type) {
if (type->llvm_di_type != nullptr) return;
ZigType *payload_type = type->data.error_union.payload_type;
ZigType *err_set_type = type->data.error_union.err_set_type;
if (!type_has_bits(payload_type)) {
assert(type_has_bits(err_set_type));
type->llvm_type = get_llvm_type(g, err_set_type);
type->llvm_di_type = get_llvm_di_type(g, err_set_type);
} else if (!type_has_bits(err_set_type)) {
type->llvm_type = get_llvm_type(g, payload_type);
type->llvm_di_type = get_llvm_di_type(g, payload_type);
} else {
LLVMTypeRef err_set_llvm_type = get_llvm_type(g, err_set_type);
LLVMTypeRef payload_llvm_type = get_llvm_type(g, payload_type);
LLVMTypeRef elem_types[2];
elem_types[err_union_err_index] = err_set_llvm_type;
elem_types[err_union_payload_index] = payload_llvm_type;
type->llvm_type = LLVMStructType(elem_types, 2, false);
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
compile_unit_scope, di_file, line);
uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_llvm_type);
uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_llvm_type);
uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, err_union_err_index);
uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_llvm_type);
uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_llvm_type);
uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type,
err_union_payload_index);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
ZigLLVMDIType *di_element_types[] = {
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"tag", di_file, line,
tag_debug_size_in_bits,
tag_debug_align_in_bits,
tag_offset_in_bits,
0, get_llvm_di_type(g, err_set_type)),
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
"value", di_file, line,
value_debug_size_in_bits,
value_debug_align_in_bits,
value_offset_in_bits,
0, get_llvm_di_type(g, payload_type)),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
compile_unit_scope,
buf_ptr(&type->name),
di_file, line,
debug_size_in_bits,
debug_align_in_bits,
0,
nullptr, di_element_types, 2, 0, nullptr, "");
ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
type->llvm_di_type = replacement_di_type;
}
}
static void resolve_llvm_types_array(CodeGen *g, ZigType *type) {
if (type->llvm_di_type != nullptr) return;
if (!type_has_bits(type)) {
type->llvm_type = g->builtin_types.entry_void->llvm_type;
type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
return;
}
ZigType *elem_type = type->data.array.child_type;
// TODO https://github.com/ziglang/zig/issues/1424
type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)type->data.array.len);
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
type->llvm_di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, debug_size_in_bits,
debug_align_in_bits, get_llvm_di_type(g, elem_type), (int)type->data.array.len);
}
static void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type) {
if (fn_type->llvm_di_type != nullptr) return;
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
bool first_arg_return = want_first_arg_sret(g, fn_type_id);
bool is_async = fn_type_id->cc == CallingConventionAsync;
bool is_c_abi = fn_type_id->cc == CallingConventionC;
bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id);
// +1 for maybe making the first argument the return value
// +1 for maybe first argument the error return trace
// +2 for maybe arguments async allocator and error code pointer
ZigList<LLVMTypeRef> gen_param_types = {};
// +1 because 0 is the return type and
// +1 for maybe making first arg ret val and
// +1 for maybe first argument the error return trace
// +2 for maybe arguments async allocator and error code pointer
ZigList<ZigLLVMDIType *> param_di_types = {};
param_di_types.append(get_llvm_di_type(g, fn_type_id->return_type));
ZigType *gen_return_type;
if (is_async) {
gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false);
} else if (!type_has_bits(fn_type_id->return_type)) {
gen_return_type = g->builtin_types.entry_void;
} else if (first_arg_return) {
ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
gen_param_types.append(get_llvm_type(g, gen_type));
param_di_types.append(get_llvm_di_type(g, gen_type));
gen_return_type = g->builtin_types.entry_void;
} else {
gen_return_type = fn_type_id->return_type;
}
fn_type->data.fn.gen_return_type = gen_return_type;
if (prefix_arg_error_return_trace) {
ZigType *gen_type = get_ptr_to_stack_trace_type(g);
gen_param_types.append(get_llvm_type(g, gen_type));
param_di_types.append(get_llvm_di_type(g, gen_type));
}
if (is_async) {
{
// async allocator param
ZigType *gen_type = fn_type_id->async_allocator_type;
gen_param_types.append(get_llvm_type(g, gen_type));
param_di_types.append(get_llvm_di_type(g, gen_type));
}
{
// error code pointer
ZigType *gen_type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false);
gen_param_types.append(get_llvm_type(g, gen_type));
param_di_types.append(get_llvm_di_type(g, gen_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];
ZigType *type_entry = src_param_info->type;
FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i];
gen_param_info->src_index = i;
gen_param_info->gen_index = SIZE_MAX;
if (is_c_abi || !type_has_bits(type_entry))
continue;
ZigType *gen_type;
if (handle_is_ptr(type_entry)) {
gen_type = get_pointer_to_type(g, type_entry, true);
gen_param_info->is_byval = true;
} else {
gen_type = type_entry;
}
gen_param_info->gen_index = gen_param_types.length;
gen_param_info->type = gen_type;
gen_param_types.append(get_llvm_type(g, gen_type));
param_di_types.append(get_llvm_di_type(g, gen_type));
}
if (is_c_abi) {
FnWalk fn_walk = {};
fn_walk.id = FnWalkIdTypes;
fn_walk.data.types.param_di_types = &param_di_types;
fn_walk.data.types.gen_param_types = &gen_param_types;
walk_function_params(g, fn_type, &fn_walk);
}
fn_type->data.fn.gen_param_count = gen_param_types.length;
for (size_t i = 0; i < gen_param_types.length; i += 1) {
assert(gen_param_types.items[i] != nullptr);
}
fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
fn_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, fn_type->data.fn.raw_di_type,
LLVMStoreSizeOfType(g->target_data_ref, fn_type->llvm_type),
LLVMABIAlignmentOfType(g->target_data_ref, fn_type->llvm_type), "");
}
static void resolve_llvm_types_anyerror(CodeGen *g) {
ZigType *entry = g->builtin_types.entry_global_error_set;
entry->llvm_type = get_llvm_type(g, g->err_tag_type);
ZigList<ZigLLVMDIEnumerator *> err_enumerators = {};
// reserve index 0 to indicate no error
err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, "(none)", 0));
for (size_t i = 1; i < g->errors_by_index.length; i += 1) {
ErrorTableEntry *error_entry = g->errors_by_index.at(i);
err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(&error_entry->name), i));
}
// create debug type for error sets
uint64_t tag_debug_size_in_bits = g->err_tag_type->size_in_bits;
uint64_t tag_debug_align_in_bits = 8*g->err_tag_type->abi_align;
ZigLLVMDIFile *err_set_di_file = nullptr;
entry->llvm_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
ZigLLVMCompileUnitToScope(g->compile_unit), buf_ptr(&entry->name),
err_set_di_file, 0,
tag_debug_size_in_bits,
tag_debug_align_in_bits,
err_enumerators.items, err_enumerators.length,
get_llvm_di_type(g, g->err_tag_type), "");
}
static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
assert(type->id == ZigTypeIdOpaque || type_is_resolved(type, ResolveStatusSizeKnown));
assert(wanted_resolve_status > ResolveStatusSizeKnown);
switch (type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
zig_unreachable();
case ZigTypeIdFloat:
case ZigTypeIdOpaque:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
assert(type->llvm_di_type != nullptr);
return;
case ZigTypeIdStruct:
if (type->data.structure.is_slice)
return resolve_llvm_types_slice(g, type, wanted_resolve_status);
else
return resolve_llvm_types_struct(g, type, wanted_resolve_status);
case ZigTypeIdEnum:
return resolve_llvm_types_enum(g, type);
case ZigTypeIdUnion:
return resolve_llvm_types_union(g, type, wanted_resolve_status);
case ZigTypeIdPointer:
return resolve_llvm_types_pointer(g, type);
case ZigTypeIdPromise: {
if (type->llvm_di_type != nullptr) return;
ZigType *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false);
type->llvm_type = get_llvm_type(g, u8_ptr_type);
type->llvm_di_type = get_llvm_di_type(g, u8_ptr_type);
return;
}
case ZigTypeIdInt:
return resolve_llvm_types_integer(g, type);
case ZigTypeIdOptional:
return resolve_llvm_types_optional(g, type);
case ZigTypeIdErrorUnion:
return resolve_llvm_types_error_union(g, type);
case ZigTypeIdArray:
return resolve_llvm_types_array(g, type);
case ZigTypeIdFn:
return resolve_llvm_types_fn(g, type);
case ZigTypeIdErrorSet: {
if (type->llvm_di_type != nullptr) return;
if (g->builtin_types.entry_global_error_set->llvm_type == nullptr) {
resolve_llvm_types_anyerror(g);
}
type->llvm_type = g->builtin_types.entry_global_error_set->llvm_type;
type->llvm_di_type = g->builtin_types.entry_global_error_set->llvm_di_type;
return;
}
case ZigTypeIdVector: {
if (type->llvm_di_type != nullptr) return;
type->llvm_type = LLVMVectorType(get_llvm_type(g, type->data.vector.elem_type), type->data.vector.len);
type->llvm_di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, type->size_in_bits,
type->abi_align, get_llvm_di_type(g, type->data.vector.elem_type), type->data.vector.len);
return;
}
}
zig_unreachable();
}
LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type) {
assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
assert(type->abi_size == 0 || type->abi_size == LLVMABISizeOfType(g->target_data_ref, type->llvm_type));
assert(type->abi_align == 0 || type->abi_align == LLVMABIAlignmentOfType(g->target_data_ref, type->llvm_type));
return type->llvm_type;
}
ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type) {
assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
return type->llvm_di_type;
}
void src_assert(bool ok, AstNode *source_node) {
if (ok) return;
if (source_node == nullptr) {
fprintf(stderr, "when analyzing (unknown source location): ");
} else {
fprintf(stderr, "when analyzing %s:%u:%u: ",
buf_ptr(source_node->owner->data.structure.root_struct->path),
(unsigned)source_node->line + 1, (unsigned)source_node->column + 1);
}
const char *msg = "assertion failed";
stage2_panic(msg, strlen(msg));
}