/* * 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 "parser.hpp" #include "error.hpp" #include "zig_llvm.hpp" #include "os.hpp" #include "parseh.hpp" #include "config.h" #include "ast_render.hpp" #include "eval.hpp" static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only); static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type); static TypeTableEntry *unwrapped_node_type(AstNode *node); static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node); static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, Buf *err_name); static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, bool depends_on_compile_var); static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node, TypeTableEntry *type_entry, bool depends_on_compile_var); static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type, bool depends_on_compile_var); static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node, TypeTableEntry *expected_type, uint64_t x, bool depends_on_compile_var); static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value, bool depends_on_compile_var); static AstNode *find_decl(BlockContext *context, Buf *name); static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only, BlockContext *block_context, bool depends_on_compile_var); static TopLevelDecl *get_as_top_level_decl(AstNode *node); static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr); static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node); static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); static void resolve_use_decl(CodeGen *g, AstNode *node); static void preview_use_decl(CodeGen *g, AstNode *node); static AstNode *first_executing_node(AstNode *node) { switch (node->type) { case NodeTypeFnCallExpr: return first_executing_node(node->data.fn_call_expr.fn_ref_expr); case NodeTypeBinOpExpr: return first_executing_node(node->data.bin_op_expr.op1); case NodeTypeUnwrapErrorExpr: return first_executing_node(node->data.unwrap_err_expr.op1); case NodeTypeArrayAccessExpr: return first_executing_node(node->data.array_access_expr.array_ref_expr); case NodeTypeSliceExpr: return first_executing_node(node->data.slice_expr.array_ref_expr); case NodeTypeFieldAccessExpr: return first_executing_node(node->data.field_access_expr.struct_expr); case NodeTypeSwitchRange: return first_executing_node(node->data.switch_range.start); case NodeTypeRoot: case NodeTypeFnProto: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeBlock: case NodeTypeDirective: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeVariableDeclaration: case NodeTypeTypeDecl: case NodeTypeErrorValueDecl: case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeUse: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeContainerDecl: case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeTypeLiteral: case NodeTypeContainerInitExpr: return node; } zig_unreachable(); } static void mark_impure_fn(BlockContext *context) { if (context->fn_entry) { context->fn_entry->is_pure = false; } } ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { // if this assert fails, then parseh generated code that // failed semantic analysis, which isn't supposed to happen assert(!node->owner->c_import_node); // if an error occurs in a function then it becomes impure if (node->block_context) { FnTableEntry *fn_entry = node->block_context->fn_entry; if (fn_entry) { fn_entry->is_pure = false; } } ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, node->owner->source_code, node->owner->line_offsets, msg); g->errors.append(err); return err; } ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { // if this assert fails, then parseh generated code that // failed semantic analysis, which isn't supposed to happen assert(!node->owner->c_import_node); ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, node->owner->source_code, node->owner->line_offsets, msg); err_msg_add_note(parent_msg, err); return err; } TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { TypeTableEntry *entry = allocate(1); entry->arrays_by_size.init(2); entry->id = id; return entry; } static BlockContext **get_container_block_context_ptr(TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdStruct) { return &type_entry->data.structure.block_context; } else if (type_entry->id == TypeTableEntryIdEnum) { return &type_entry->data.enumeration.block_context; } else if (type_entry->id == TypeTableEntryIdUnion) { return &type_entry->data.unionation.block_context; } zig_unreachable(); } static BlockContext *get_container_block_context(TypeTableEntry *type_entry) { return *get_container_block_context_ptr(type_entry); } static TypeTableEntry *new_container_type_entry(TypeTableEntryId id, AstNode *source_node, BlockContext *parent_context) { TypeTableEntry *entry = new_type_table_entry(id); *get_container_block_context_ptr(entry) = new_block_context(source_node, parent_context); return entry; } static int bits_needed_for_unsigned(uint64_t x) { if (x <= UINT8_MAX) { return 8; } else if (x <= UINT16_MAX) { return 16; } else if (x <= UINT32_MAX) { return 32; } else { return 64; } } static bool type_is_complete(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdStruct: return type_entry->data.structure.complete; case TypeTableEntryIdEnum: return type_entry->data.enumeration.complete; case TypeTableEntryIdUnion: return type_entry->data.unionation.complete; case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: return true; } zig_unreachable(); } uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) { if (type_has_bits(type_entry)) { return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); } else { return 0; } } static bool is_u8(TypeTableEntry *type) { return type->id == TypeTableEntryIdInt && !type->data.integral.is_signed && type->data.integral.bit_count == 8; } static bool is_slice(TypeTableEntry *type) { return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice; } TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { return get_int_type(g, false, false, bits_needed_for_unsigned(x)); } static TypeTableEntry *get_generic_fn_type(CodeGen *g, AstNode *decl_node) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdGenericFn); buf_init_from_str(&entry->name, "(generic function)"); entry->deep_const = true; entry->zero_bits = true; entry->data.generic_fn.decl_node = decl_node; return entry; } TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { assert(child_type->id != TypeTableEntryIdInvalid); TypeTableEntry **parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)]; if (*parent_pointer) { return *parent_pointer; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); entry->deep_const = is_const && child_type->deep_const; const char *const_str = is_const ? "const " : ""; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name)); TypeTableEntry *canon_child_type = get_underlying_type(child_type); assert(canon_child_type->id != TypeTableEntryIdInvalid); if (type_is_complete(canon_child_type)) { entry->zero_bits = !type_has_bits(canon_child_type); } else { entry->zero_bits = false; } if (!entry->zero_bits) { entry->type_ref = LLVMPointerType(child_type->type_ref, 0); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); assert(child_type->di_type); entry->di_type = LLVMZigCreateDebugPointerType(g->dbuilder, child_type->di_type, debug_size_in_bits, debug_align_in_bits, buf_ptr(&entry->name)); } entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; *parent_pointer = entry; return entry; } } TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->maybe_parent) { TypeTableEntry *entry = child_type->maybe_parent; return entry; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe); assert(child_type->type_ref); assert(child_type->di_type); entry->deep_const = child_type->deep_const; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); if (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn) { // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers // function types are technically pointers entry->type_ref = child_type->type_ref; entry->di_type = child_type->di_type; } else { // create a struct with a boolean whether this is the null value LLVMTypeRef elem_types[] = { child_type->type_ref, LLVMInt1Type(), }; entry->type_ref = LLVMStructType(elem_types, 2, false); LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); LLVMZigDIFile *di_file = nullptr; unsigned line = 0; entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), compile_unit_scope, di_file, line); uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref); uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref); uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); TypeTableEntry *bool_type = g->builtin_types.entry_bool; uint64_t maybe_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, bool_type->type_ref); uint64_t maybe_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, bool_type->type_ref); uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); LLVMZigDIType *di_element_types[] = { LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), "val", di_file, line, val_debug_size_in_bits, val_debug_align_in_bits, val_offset_in_bits, 0, child_type->di_type), LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), "maybe", di_file, line, maybe_debug_size_in_bits, maybe_debug_align_in_bits, maybe_offset_in_bits, 0, child_type->di_type), }; LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 2, 0, nullptr, ""); LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; } entry->data.maybe.child_type = child_type; child_type->maybe_parent = entry; return entry; } } static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->error_parent) { return child_type->error_parent; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); assert(child_type->type_ref); assert(child_type->di_type); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "%%%s", buf_ptr(&child_type->name)); entry->data.error.child_type = child_type; entry->deep_const = child_type->deep_const; if (!type_has_bits(child_type)) { entry->type_ref = g->err_tag_type->type_ref; entry->di_type = g->err_tag_type->di_type; } else { LLVMTypeRef elem_types[] = { g->err_tag_type->type_ref, child_type->type_ref, }; entry->type_ref = LLVMStructType(elem_types, 2, false); LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); LLVMZigDIFile *di_file = nullptr; unsigned line = 0; entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), compile_unit_scope, di_file, line); uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, g->err_tag_type->type_ref); uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref); uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref); uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); LLVMZigDIType *di_element_types[] = { LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), "tag", di_file, line, tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, 0, child_type->di_type), LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), "value", di_file, line, value_debug_size_in_bits, value_debug_align_in_bits, value_offset_in_bits, 0, child_type->di_type), }; LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 2, 0, nullptr, ""); LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; } child_type->error_parent = entry; return entry; } } TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) { auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); if (existing_entry) { TypeTableEntry *entry = existing_entry->value; return entry; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); entry->type_ref = LLVMArrayType(child_type->type_ref, array_size); entry->zero_bits = (array_size == 0) || child_type->zero_bits; entry->deep_const = child_type->deep_const; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name)); if (!entry->zero_bits) { uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); entry->di_type = LLVMZigCreateDebugArrayType(g->dbuilder, debug_size_in_bits, debug_align_in_bits, child_type->di_type, array_size); } entry->data.array.child_type = child_type; entry->data.array.len = array_size; child_type->arrays_by_size.put(array_size, entry); return entry; } } static void slice_type_common_init(CodeGen *g, TypeTableEntry *child_type, bool is_const, TypeTableEntry *entry) { TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const); unsigned element_count = 2; entry->data.structure.is_packed = false; entry->data.structure.is_slice = true; entry->data.structure.src_field_count = element_count; entry->data.structure.gen_field_count = element_count; entry->data.structure.fields = allocate(element_count); entry->data.structure.fields[0].name = buf_create_from_str("ptr"); entry->data.structure.fields[0].type_entry = pointer_type; entry->data.structure.fields[0].src_index = 0; entry->data.structure.fields[0].gen_index = 0; entry->data.structure.fields[1].name = buf_create_from_str("len"); entry->data.structure.fields[1].type_entry = g->builtin_types.entry_usize; entry->data.structure.fields[1].src_index = 1; entry->data.structure.fields[1].gen_index = 1; } TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { assert(child_type->id != TypeTableEntryIdInvalid); TypeTableEntry **parent_pointer = &child_type->unknown_size_array_parent[(is_const ? 1 : 0)]; if (*parent_pointer) { return *parent_pointer; } else if (is_const) { TypeTableEntry *var_peer = get_slice_type(g, child_type, false); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); entry->deep_const = child_type->deep_const; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name)); slice_type_common_init(g, child_type, is_const, entry); entry->type_ref = var_peer->type_ref; entry->di_type = var_peer->di_type; entry->data.structure.complete = true; *parent_pointer = entry; return entry; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name)); entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name)); LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); LLVMZigDIFile *di_file = nullptr; unsigned line = 0; entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), compile_unit_scope, di_file, line); if (child_type->zero_bits) { LLVMTypeRef element_types[] = { g->builtin_types.entry_usize->type_ref, }; LLVMStructSetBody(entry->type_ref, element_types, 1, false); slice_type_common_init(g, child_type, is_const, entry); entry->data.structure.gen_field_count = 1; entry->data.structure.fields[0].gen_index = -1; entry->data.structure.fields[1].gen_index = 0; TypeTableEntry *usize_type = g->builtin_types.entry_usize; uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); LLVMZigDIType *di_element_types[] = { LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), "len", di_file, line, len_debug_size_in_bits, len_debug_align_in_bits, len_offset_in_bits, 0, usize_type->di_type), }; LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 1, 0, nullptr, ""); LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; } else { TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const); unsigned element_count = 2; LLVMTypeRef element_types[] = { pointer_type->type_ref, g->builtin_types.entry_usize->type_ref, }; LLVMStructSetBody(entry->type_ref, element_types, element_count, false); slice_type_common_init(g, child_type, is_const, entry); uint64_t ptr_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, pointer_type->type_ref); uint64_t ptr_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, pointer_type->type_ref); uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); TypeTableEntry *usize_type = g->builtin_types.entry_usize; uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, usize_type->type_ref); uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); LLVMZigDIType *di_element_types[] = { LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), "ptr", di_file, line, ptr_debug_size_in_bits, ptr_debug_align_in_bits, ptr_offset_in_bits, 0, pointer_type->di_type), LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), "len", di_file, line, len_debug_size_in_bits, len_debug_align_in_bits, len_offset_in_bits, 0, usize_type->di_type), }; LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, compile_unit_scope, buf_ptr(&entry->name), di_file, line, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, 2, 0, nullptr, ""); LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); entry->di_type = replacement_di_type; } entry->data.structure.complete = true; *parent_pointer = entry; return entry; } } TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdTypeDecl); buf_init_from_str(&entry->name, name); entry->deep_const = child_type->deep_const; entry->type_ref = child_type->type_ref; entry->di_type = child_type->di_type; entry->zero_bits = child_type->zero_bits; entry->data.type_decl.child_type = child_type; if (child_type->id == TypeTableEntryIdTypeDecl) { entry->data.type_decl.canonical_type = child_type->data.type_decl.canonical_type; } else { entry->data.type_decl.canonical_type = child_type; } return entry; } // accepts ownership of fn_type_id memory TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { auto table_entry = g->fn_type_table.maybe_get(fn_type_id); if (table_entry) { return table_entry->value; } TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); fn_type->deep_const = true; fn_type->data.fn.fn_type_id = *fn_type_id; if (fn_type_id->param_info == &fn_type_id->prealloc_param_info[0]) { fn_type->data.fn.fn_type_id.param_info = &fn_type->data.fn.fn_type_id.prealloc_param_info[0]; } if (fn_type_id->is_cold) { fn_type->data.fn.calling_convention = LLVMColdCallConv; } else if (fn_type_id->is_extern) { fn_type->data.fn.calling_convention = LLVMCCallConv; } else { fn_type->data.fn.calling_convention = LLVMFastCallConv; } // populate the name of the type buf_resize(&fn_type->name, 0); const char *extern_str = fn_type_id->is_extern ? "extern " : ""; const char *naked_str = fn_type_id->is_naked ? "naked " : ""; const char *cold_str = fn_type_id->is_cold ? "cold " : ""; buf_appendf(&fn_type->name, "%s%s%sfn(", extern_str, naked_str, cold_str); for (int i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; TypeTableEntry *param_type = param_info->type; const char *comma = (i == 0) ? "" : ", "; const char *noalias_str = param_info->is_noalias ? "noalias " : ""; buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(¶m_type->name)); } if (fn_type_id->is_var_args) { const char *comma = (fn_type_id->param_count == 0) ? "" : ", "; buf_appendf(&fn_type->name, "%s...", comma); } buf_appendf(&fn_type->name, ")"); if (fn_type_id->return_type->id != TypeTableEntryIdVoid) { buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name)); } // next, loop over the parameters again and compute debug information // and codegen information bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type); // +1 for maybe making the first argument the return value LLVMTypeRef *gen_param_types = allocate(1 + fn_type_id->param_count); // +1 because 0 is the return type and +1 for maybe making first arg ret val LLVMZigDIType **param_di_types = allocate(2 + fn_type_id->param_count); param_di_types[0] = fn_type_id->return_type->di_type; int gen_param_index = 0; TypeTableEntry *gen_return_type; if (!type_has_bits(fn_type_id->return_type)) { gen_return_type = g->builtin_types.entry_void; } else if (first_arg_return) { TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false); gen_param_types[gen_param_index] = gen_type->type_ref; gen_param_index += 1; // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; gen_return_type = g->builtin_types.entry_void; } else { gen_return_type = fn_type_id->return_type; } fn_type->data.fn.gen_return_type = gen_return_type; fn_type->data.fn.gen_param_info = allocate(fn_type_id->param_count); for (int i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i]; TypeTableEntry *type_entry = src_param_info->type; FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i]; gen_param_info->src_index = i; gen_param_info->gen_index = -1; assert(type_is_complete(type_entry)); if (type_has_bits(type_entry)) { TypeTableEntry *gen_type; if (handle_is_ptr(type_entry)) { gen_type = get_pointer_to_type(g, type_entry, true); gen_param_info->is_byval = true; } else { gen_type = type_entry; } gen_param_types[gen_param_index] = gen_type->type_ref; gen_param_info->gen_index = gen_param_index; gen_param_info->type = gen_type; gen_param_index += 1; // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; } } fn_type->data.fn.gen_param_count = gen_param_index; fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, gen_param_types, gen_param_index, fn_type_id->is_var_args); fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, param_di_types, gen_param_index + 1, 0); g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type); return fn_type; } static TypeTableEntryId container_to_type(ContainerKind kind) { switch (kind) { case ContainerKindStruct: return TypeTableEntryIdStruct; case ContainerKindEnum: return TypeTableEntryIdEnum; case ContainerKindUnion: return TypeTableEntryIdUnion; } zig_unreachable(); } TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, ContainerKind kind, AstNode *decl_node, const char *name) { TypeTableEntryId type_id = container_to_type(kind); TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, context); switch (kind) { case ContainerKindStruct: entry->data.structure.decl_node = decl_node; break; case ContainerKindEnum: entry->data.enumeration.decl_node = decl_node; break; case ContainerKindUnion: entry->data.unionation.decl_node = decl_node; break; } unsigned line = decl_node ? decl_node->line : 0; entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name); entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, LLVMZigTag_DW_structure_type(), name, LLVMZigFileToScope(import->di_file), import->di_file, line + 1); buf_init_from_str(&entry->name, name); return entry; } TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdTypeDecl) { return type_entry->data.type_decl.canonical_type; } else { return type_entry; } } // If the node does not have a constant expression value with a metatype, generates an error // and returns invalid type. Otherwise, returns the type of the constant expression value. // Must be called after analyze_expression on the same node. static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { if (node->type == NodeTypeSymbol && node->data.symbol_expr.override_type_entry) { return node->data.symbol_expr.override_type_entry; } Expr *expr = get_resolved_expr(node); assert(expr->type_entry); if (expr->type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (expr->type_entry->id == TypeTableEntryIdMetaType) { // OK } else { add_node_error(g, node, buf_sprintf("expected type, found expression")); return g->builtin_types.entry_invalid; } ConstExprValue *const_val = &expr->const_val; if (!const_val->ok) { add_node_error(g, node, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } return const_val->data.x_type; } static TypeTableEntry *analyze_type_expr_pointer_only(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, bool pointer_only) { AstNode **node_ptr = node->parent_field; analyze_expression_pointer_only(g, import, context, nullptr, *node_ptr, pointer_only); return resolve_type(g, *node_ptr); } // Calls analyze_expression on node, and then resolve_type. static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { return analyze_type_expr_pointer_only(g, import, context, node, false); } static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool is_naked, bool is_cold) { assert(node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &node->data.fn_proto; if (fn_proto->skip) { return g->builtin_types.entry_invalid; } FnTypeId fn_type_id = {0}; fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport); fn_type_id.is_naked = is_naked; fn_type_id.is_cold = is_cold; fn_type_id.is_inline = fn_proto->is_inline; fn_type_id.param_count = fn_proto->params.length; if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) { fn_type_id.param_info = allocate_nonzero(fn_type_id.param_count); } else { fn_type_id.param_info = &fn_type_id.prealloc_param_info[0]; } fn_type_id.is_var_args = fn_proto->is_var_args; fn_type_id.return_type = analyze_type_expr(g, import, context, fn_proto->return_type); switch (fn_type_id.return_type->id) { case TypeTableEntryIdInvalid: fn_proto->skip = true; break; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: fn_proto->skip = true; add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name))); break; case TypeTableEntryIdMetaType: if (!fn_proto->is_inline) { fn_proto->skip = true; add_node_error(g, fn_proto->return_type, buf_sprintf("function with return type '%s' must be declared inline", buf_ptr(&fn_type_id.return_type->name))); return g->builtin_types.entry_invalid; } break; case TypeTableEntryIdUnreachable: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: break; } for (int i = 0; i < fn_type_id.param_count; i += 1) { AstNode *child = fn_proto->params.at(i); assert(child->type == NodeTypeParamDecl); TypeTableEntry *type_entry = analyze_type_expr(g, import, context, child->data.param_decl.type); switch (type_entry->id) { case TypeTableEntryIdInvalid: fn_proto->skip = true; break; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdUnreachable: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: fn_proto->skip = true; add_node_error(g, child->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); break; case TypeTableEntryIdMetaType: if (!child->data.param_decl.is_inline) { fn_proto->skip = true; add_node_error(g, child->data.param_decl.type, buf_sprintf("parameter of type '%s' must be declared inline", buf_ptr(&type_entry->name))); } break; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: break; } if (type_entry->id == TypeTableEntryIdInvalid) { fn_proto->skip = true; } FnTypeParamInfo *param_info = &fn_type_id.param_info[i]; param_info->type = type_entry; param_info->is_noalias = child->data.param_decl.is_noalias; } if (fn_proto->skip) { return g->builtin_types.entry_invalid; } return get_fn_type(g, &fn_type_id); } static Buf *resolve_const_expr_str(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode **node) { TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *node); if (resolved_type->id == TypeTableEntryIdInvalid) { return nullptr; } ConstExprValue *const_str_val = &get_resolved_expr(*node)->const_val; if (!const_str_val->ok) { add_node_error(g, *node, buf_sprintf("unable to evaluate constant expression")); return nullptr; } ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; uint64_t len = ptr_field->data.x_ptr.len; Buf *result = buf_alloc(); for (uint64_t i = 0; i < len; i += 1) { ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i]; uint64_t big_c = char_val->data.x_bignum.data.x_uint; assert(big_c <= UINT8_MAX); uint8_t c = big_c; buf_append_char(result, c); } return result; } static bool resolve_const_expr_bool(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode **node, bool *value) { TypeTableEntry *resolved_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *node); if (resolved_type->id == TypeTableEntryIdInvalid) { return false; } ConstExprValue *const_bool_val = &get_resolved_expr(*node)->const_val; if (!const_bool_val->ok) { add_node_error(g, *node, buf_sprintf("unable to evaluate constant expression")); return false; } *value = const_bool_val->data.x_bool; return true; } static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, ImportTableEntry *import, BlockContext *containing_context) { assert(node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &node->data.fn_proto; if (fn_proto->skip) { return; } bool is_cold = false; bool is_naked = false; bool is_test = false; bool is_noinline = false; if (fn_proto->top_level_decl.directives) { for (int i = 0; i < fn_proto->top_level_decl.directives->length; i += 1) { AstNode *directive_node = fn_proto->top_level_decl.directives->at(i); Buf *name = &directive_node->data.directive.name; if (buf_eql_str(name, "attribute")) { if (fn_table_entry->fn_def_node) { Buf *attr_name = resolve_const_expr_str(g, import, import->block_context, &directive_node->data.directive.expr); if (attr_name) { if (buf_eql_str(attr_name, "naked")) { is_naked = true; } else if (buf_eql_str(attr_name, "noinline")) { is_noinline = true; } else if (buf_eql_str(attr_name, "cold")) { is_cold = true; } else if (buf_eql_str(attr_name, "test")) { is_test = true; g->test_fn_count += 1; } else { add_node_error(g, directive_node, buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); } } } else { add_node_error(g, directive_node, buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); } } else if (buf_eql_str(name, "debug_safety")) { if (!fn_table_entry->fn_def_node) { add_node_error(g, directive_node, buf_sprintf("#debug_safety valid only on function definitions")); } else { bool enable; bool ok = resolve_const_expr_bool(g, import, import->block_context, &directive_node->data.directive.expr, &enable); if (ok && !enable) { fn_table_entry->safety_off = true; } } } else if (buf_eql_str(name, "condition")) { if (fn_proto->top_level_decl.visib_mod == VisibModExport) { bool include; bool ok = resolve_const_expr_bool(g, import, import->block_context, &directive_node->data.directive.expr, &include); if (ok && !include) { fn_proto->top_level_decl.visib_mod = VisibModPub; } } else { add_node_error(g, directive_node, buf_sprintf("#condition valid only on exported symbols")); } } else if (buf_eql_str(name, "static_eval_enable")) { if (!fn_table_entry->fn_def_node) { add_node_error(g, directive_node, buf_sprintf("#static_val_enable valid only on function definitions")); } else { bool enable; bool ok = resolve_const_expr_bool(g, import, import->block_context, &directive_node->data.directive.expr, &enable); if (!enable || !ok) { fn_table_entry->want_pure = WantPureFalse; } // TODO cause compile error if enable is true and impure fn } } else { add_node_error(g, directive_node, buf_sprintf("invalid directive: '%s'", buf_ptr(name))); } } } bool is_internal = (fn_proto->top_level_decl.visib_mod != VisibModExport); bool is_c_compat = !is_internal || fn_proto->is_extern; fn_table_entry->internal_linkage = !is_c_compat; TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, containing_context, nullptr, node, is_naked, is_cold); fn_table_entry->type_entry = fn_type; fn_table_entry->is_test = is_test; fn_table_entry->is_noinline = is_noinline; if (fn_type->id == TypeTableEntryIdInvalid) { fn_proto->skip = true; return; } if (fn_proto->is_inline && fn_table_entry->is_noinline) { add_node_error(g, node, buf_sprintf("function is both inline and noinline")); fn_proto->skip = true; return; } Buf *symbol_name; if (is_c_compat) { symbol_name = &fn_table_entry->symbol_name; } else { symbol_name = buf_sprintf("_%s", buf_ptr(&fn_table_entry->symbol_name)); } if (fn_table_entry->fn_def_node) { BlockContext *context = new_block_context(fn_table_entry->fn_def_node, containing_context); fn_table_entry->fn_def_node->data.fn_def.block_context = context; } fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_type->data.fn.raw_type_ref); if (fn_proto->is_inline) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute); } if (fn_table_entry->is_noinline) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoInlineAttribute); } if (fn_type->data.fn.fn_type_id.is_naked) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNakedAttribute); } LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage); if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute); } LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention); if (!fn_table_entry->is_extern) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoUnwindAttribute); } if (!g->is_release_build && !fn_proto->is_inline) { ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim", "true"); ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim-non-leaf", nullptr); } if (fn_table_entry->fn_def_node) { // Add debug info. unsigned line_number = node->line + 1; unsigned scope_line = line_number; bool is_definition = fn_table_entry->fn_def_node != nullptr; unsigned flags = 0; bool is_optimized = g->is_release_build; LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder, containing_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "", import->di_file, line_number, fn_type->di_type, fn_table_entry->internal_linkage, is_definition, scope_line, flags, is_optimized, nullptr); fn_table_entry->fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram); ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram); } } static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type) { // if you change this logic you likely must also change similar logic in parseh.cpp assert(enum_type->id == TypeTableEntryIdEnum); AstNode *decl_node = enum_type->data.enumeration.decl_node; if (enum_type->data.enumeration.embedded_in_current) { if (!enum_type->data.enumeration.reported_infinite_err) { enum_type->data.enumeration.reported_infinite_err = true; add_node_error(g, decl_node, buf_sprintf("enum has infinite size")); } return; } if (enum_type->data.enumeration.fields) { // we already resolved this type. skip return; } assert(decl_node->type == NodeTypeContainerDecl); assert(enum_type->di_type); enum_type->deep_const = true; uint32_t field_count = decl_node->data.struct_decl.fields.length; enum_type->data.enumeration.field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); LLVMZigDIEnumerator **di_enumerators = allocate(field_count); // we possibly allocate too much here since gen_field_count can be lower than field_count. // the only problem is potential wasted space though. LLVMZigDIType **union_inner_di_types = allocate(field_count); TypeTableEntry *biggest_union_member = nullptr; uint64_t biggest_align_in_bits = 0; uint64_t biggest_union_member_size_in_bits = 0; BlockContext *context = enum_type->data.enumeration.block_context; // set temporary flag enum_type->data.enumeration.embedded_in_current = true; int gen_field_index = 0; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.struct_decl.fields.at(i); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; type_enum_field->name = &field_node->data.struct_field.name; TypeTableEntry *field_type = analyze_type_expr(g, import, context, field_node->data.struct_field.type); type_enum_field->type_entry = field_type; type_enum_field->value = i; if (!field_type->deep_const) { enum_type->deep_const = false; } di_enumerators[i] = LLVMZigCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i); if (field_type->id == TypeTableEntryIdStruct) { resolve_struct_type(g, import, field_type); } else if (field_type->id == TypeTableEntryIdEnum) { resolve_enum_type(g, import, field_type); } else if (field_type->id == TypeTableEntryIdInvalid) { enum_type->data.enumeration.is_invalid = true; continue; } else if (!type_has_bits(field_type)) { continue; } uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref); union_inner_di_types[gen_field_index] = LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(enum_type->di_type), buf_ptr(type_enum_field->name), import->di_file, field_node->line + 1, debug_size_in_bits, debug_align_in_bits, 0, 0, field_type->di_type); biggest_align_in_bits = max(biggest_align_in_bits, debug_align_in_bits); if (!biggest_union_member || debug_size_in_bits > biggest_union_member_size_in_bits) { biggest_union_member = field_type; biggest_union_member_size_in_bits = debug_size_in_bits; } gen_field_index += 1; } // unset temporary flag enum_type->data.enumeration.embedded_in_current = false; enum_type->data.enumeration.complete = true; if (!enum_type->data.enumeration.is_invalid) { enum_type->data.enumeration.gen_field_count = gen_field_index; enum_type->data.enumeration.union_type = biggest_union_member; TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); enum_type->data.enumeration.tag_type = tag_type_entry; if (biggest_union_member) { // create llvm type for union LLVMTypeRef union_element_type = biggest_union_member->type_ref; LLVMTypeRef union_type_ref = LLVMStructType(&union_element_type, 1, false); // create llvm type for root struct LLVMTypeRef root_struct_element_types[] = { tag_type_entry->type_ref, union_type_ref, }; LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); // create debug type for tag uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref); LLVMZigDIType *tag_di_type = LLVMZigCreateDebugEnumerationType(g->dbuilder, LLVMZigTypeToScope(enum_type->di_type), "AnonEnum", import->di_file, decl_node->line + 1, tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, tag_type_entry->di_type, ""); // create debug type for union LLVMZigDIType *union_di_type = LLVMZigCreateDebugUnionType(g->dbuilder, LLVMZigTypeToScope(enum_type->di_type), "AnonUnion", import->di_file, decl_node->line + 1, biggest_union_member_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, gen_field_index, 0, ""); // create debug types for members of root struct uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, 0); LLVMZigDIType *tag_member_di_type = LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(enum_type->di_type), "tag_field", import->di_file, decl_node->line + 1, tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, 0, tag_di_type); uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, 1); LLVMZigDIType *union_member_di_type = LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(enum_type->di_type), "union_field", import->di_file, decl_node->line + 1, biggest_union_member_size_in_bits, biggest_align_in_bits, union_offset_in_bits, 0, union_di_type); // create debug type for root struct LLVMZigDIType *di_root_members[] = { tag_member_di_type, union_member_di_type, }; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, LLVMZigFileToScope(import->di_file), buf_ptr(&decl_node->data.struct_decl.name), import->di_file, decl_node->line + 1, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_root_members, 2, 0, nullptr, ""); LLVMZigReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); enum_type->di_type = replacement_di_type; } else { // create llvm type for root struct enum_type->type_ref = tag_type_entry->type_ref; // create debug type for tag uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, tag_type_entry->type_ref); LLVMZigDIType *tag_di_type = LLVMZigCreateDebugEnumerationType(g->dbuilder, LLVMZigFileToScope(import->di_file), buf_ptr(&decl_node->data.struct_decl.name), import->di_file, decl_node->line + 1, tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, tag_type_entry->di_type, ""); LLVMZigReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); enum_type->di_type = tag_di_type; } } } static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type) { // if you change the logic of this function likely you must make a similar change in // parseh.cpp assert(struct_type->id == TypeTableEntryIdStruct); AstNode *decl_node = struct_type->data.structure.decl_node; if (struct_type->data.structure.embedded_in_current) { struct_type->data.structure.is_invalid = true; if (!struct_type->data.structure.reported_infinite_err) { struct_type->data.structure.reported_infinite_err = true; add_node_error(g, decl_node, buf_sprintf("struct has infinite size")); } return; } if (struct_type->data.structure.fields) { // we already resolved this type. skip return; } assert(decl_node->type == NodeTypeContainerDecl); assert(struct_type->di_type); struct_type->deep_const = true; int field_count = decl_node->data.struct_decl.fields.length; struct_type->data.structure.src_field_count = field_count; struct_type->data.structure.fields = allocate(field_count); // we possibly allocate too much here since gen_field_count can be lower than field_count. // the only problem is potential wasted space though. LLVMTypeRef *element_types = allocate(field_count); // this field should be set to true only during the recursive calls to resolve_struct_type struct_type->data.structure.embedded_in_current = true; BlockContext *context = struct_type->data.structure.block_context; int gen_field_index = 0; for (int i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.struct_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; type_struct_field->name = &field_node->data.struct_field.name; TypeTableEntry *field_type = analyze_type_expr(g, import, context, field_node->data.struct_field.type); type_struct_field->type_entry = field_type; type_struct_field->src_index = i; type_struct_field->gen_index = -1; if (!field_type->deep_const) { struct_type->deep_const = false; } if (field_type->id == TypeTableEntryIdStruct) { resolve_struct_type(g, import, field_type); } else if (field_type->id == TypeTableEntryIdEnum) { resolve_enum_type(g, import, field_type); } else if (field_type->id == TypeTableEntryIdInvalid) { struct_type->data.structure.is_invalid = true; continue; } else if (!type_has_bits(field_type)) { continue; } type_struct_field->gen_index = gen_field_index; element_types[gen_field_index] = field_type->type_ref; assert(element_types[gen_field_index]); gen_field_index += 1; } struct_type->data.structure.embedded_in_current = false; struct_type->data.structure.gen_field_count = gen_field_index; struct_type->data.structure.complete = true; if (struct_type->data.structure.is_invalid) { return; } int gen_field_count = gen_field_index; LLVMStructSetBody(struct_type->type_ref, element_types, gen_field_count, false); LLVMZigDIType **di_element_types = allocate(gen_field_count); for (int i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.struct_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; gen_field_index = type_struct_field->gen_index; if (gen_field_index == -1) { continue; } TypeTableEntry *field_type = type_struct_field->type_entry; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, gen_field_index); di_element_types[gen_field_index] = LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), import->di_file, field_node->line + 1, debug_size_in_bits, debug_align_in_bits, debug_offset_in_bits, 0, field_type->di_type); assert(di_element_types[gen_field_index]); } uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, struct_type->type_ref); LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, LLVMZigFileToScope(import->di_file), buf_ptr(&decl_node->data.struct_decl.name), import->di_file, decl_node->line + 1, debug_size_in_bits, debug_align_in_bits, 0, nullptr, di_element_types, gen_field_count, 0, nullptr, ""); LLVMZigReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type); struct_type->di_type = replacement_di_type; struct_type->zero_bits = (debug_size_in_bits == 0); } static void resolve_union_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type) { zig_panic("TODO"); } static void get_fully_qualified_decl_name(Buf *buf, AstNode *decl_node, uint8_t sep) { TopLevelDecl *tld = get_as_top_level_decl(decl_node); AstNode *parent_decl = tld->parent_decl; if (parent_decl) { get_fully_qualified_decl_name(buf, parent_decl, sep); buf_append_char(buf, sep); buf_append_buf(buf, tld->name); } else { buf_init_from_buf(buf, tld->name); } } static void preview_generic_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeContainerDecl); if (node->data.struct_decl.generic_params_is_var_args) { add_node_error(g, node, buf_sprintf("generic parameters cannot be var args")); node->data.struct_decl.skip = true; node->data.struct_decl.generic_fn_type = g->builtin_types.entry_invalid; return; } node->data.struct_decl.generic_fn_type = get_generic_fn_type(g, node); } static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node, BlockContext *containing_context) { assert(proto_node->type == NodeTypeFnProto); if (proto_node->data.fn_proto.skip) { return; } bool is_generic_instance = proto_node->data.fn_proto.generic_proto_node; bool is_generic_fn = proto_node->data.fn_proto.inline_arg_count > 0; assert(!is_generic_instance || !is_generic_fn); AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl; Buf *proto_name = &proto_node->data.fn_proto.name; AstNode *fn_def_node = proto_node->data.fn_proto.fn_def_node; bool is_extern = proto_node->data.fn_proto.is_extern; assert(!is_extern || !is_generic_instance); if (fn_def_node && proto_node->data.fn_proto.is_var_args) { add_node_error(g, proto_node, buf_sprintf("variadic arguments only allowed in extern function declarations")); } FnTableEntry *fn_table_entry = allocate(1); fn_table_entry->import_entry = import; fn_table_entry->proto_node = proto_node; fn_table_entry->fn_def_node = fn_def_node; fn_table_entry->is_extern = is_extern; fn_table_entry->is_pure = fn_def_node != nullptr; get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_'); proto_node->data.fn_proto.fn_table_entry = fn_table_entry; if (is_generic_fn) { fn_table_entry->type_entry = get_generic_fn_type(g, proto_node); if (is_extern || proto_node->data.fn_proto.top_level_decl.visib_mod == VisibModExport) { for (int i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { AstNode *param_decl_node = proto_node->data.fn_proto.params.at(i); if (param_decl_node->data.param_decl.is_inline) { proto_node->data.fn_proto.skip = true; add_node_error(g, param_decl_node, buf_sprintf("inline parameter not allowed in extern function")); } } } } else { g->fn_protos.append(fn_table_entry); if (fn_def_node) { g->fn_defs.append(fn_table_entry); } bool is_main_fn = !is_generic_instance && !parent_decl && (import == g->root_import) && buf_eql_str(proto_name, "main"); if (is_main_fn) { g->main_fn = fn_table_entry; } resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context); if (is_main_fn && !g->link_libc) { TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; if (actual_return_type != err_void) { AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type; add_node_error(g, return_type_node, buf_sprintf("expected return type of main to be '%%void', instead is '%s'", buf_ptr(&actual_return_type->name))); } } } } static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeContainerDecl); if (node->data.struct_decl.type_entry) { // already scanned; we can ignore. This can happen from importing from an .h file. return; } Buf *name = &node->data.struct_decl.name; TypeTableEntry *container_type = get_partial_container_type(g, import, context, node->data.struct_decl.kind, node, buf_ptr(name)); node->data.struct_decl.type_entry = container_type; // handle the member function definitions independently for (int i = 0; i < node->data.struct_decl.decls.length; i += 1) { AstNode *child_node = node->data.struct_decl.decls.at(i); get_as_top_level_decl(child_node)->parent_decl = node; BlockContext *child_context = get_container_block_context(container_type); scan_decls(g, import, child_context, child_node); } } static void resolve_struct_instance(CodeGen *g, ImportTableEntry *import, AstNode *node) { TypeTableEntry *type_entry = node->data.struct_decl.type_entry; assert(type_entry); // struct/enum member fns will get resolved independently switch (node->data.struct_decl.kind) { case ContainerKindStruct: resolve_struct_type(g, import, type_entry); break; case ContainerKindEnum: resolve_enum_type(g, import, type_entry); break; case ContainerKindUnion: resolve_union_type(g, import, type_entry); break; } } static void resolve_struct_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { if (node->data.struct_decl.generic_params.length > 0) { return preview_generic_fn_proto(g, import, node); } else { return resolve_struct_instance(g, import, node); } } static void preview_error_value_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeErrorValueDecl); ErrorTableEntry *err = allocate(1); err->decl_node = node; buf_init_from_buf(&err->name, &node->data.error_value_decl.name); auto existing_entry = g->error_table.maybe_get(&err->name); if (existing_entry) { // duplicate error definitions allowed and they get the same value err->value = existing_entry->value->value; } else { int error_value_count = g->error_decls.length; assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)g->err_tag_type->data.integral.bit_count)); err->value = error_value_count; g->error_decls.append(node); g->error_table.put(&err->name, err); } node->data.error_value_decl.err = err; node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk; } static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) { TopLevelDecl *tld = get_as_top_level_decl(node); if (tld->resolution != TldResolutionUnresolved) { return; } if (pointer_only && node->type == NodeTypeContainerDecl) { return; } ImportTableEntry *import = tld->import; assert(import); if (tld->dep_loop_flag) { add_node_error(g, node, buf_sprintf("'%s' depends on itself", buf_ptr(tld->name))); tld->resolution = TldResolutionInvalid; return; } else { tld->dep_loop_flag = true; } switch (node->type) { case NodeTypeFnProto: preview_fn_proto_instance(g, import, node, node->block_context); break; case NodeTypeContainerDecl: resolve_struct_decl(g, import, node); break; case NodeTypeVariableDeclaration: { AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; VariableTableEntry *var = analyze_variable_declaration_raw(g, import, node->block_context, node, variable_declaration, false, node, false); g->global_vars.append(var); break; } case NodeTypeTypeDecl: { AstNode *type_node = node->data.type_decl.child_type; Buf *decl_name = &node->data.type_decl.symbol; TypeTableEntry *entry; if (node->data.type_decl.override_type) { entry = node->data.type_decl.override_type; } else { TypeTableEntry *child_type = analyze_type_expr(g, import, import->block_context, type_node); if (child_type->id == TypeTableEntryIdInvalid) { entry = child_type; } else { entry = get_typedecl_type(g, buf_ptr(decl_name), child_type); } } node->data.type_decl.child_type_entry = entry; break; } case NodeTypeErrorValueDecl: break; case NodeTypeUse: zig_panic("TODO resolve_top_level_decl NodeTypeUse"); break; case NodeTypeFnDef: case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: case NodeTypeUnwrapErrorExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeContainerInitExpr: case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeTypeLiteral: zig_unreachable(); } tld->resolution = TldResolutionOk; tld->dep_loop_flag = false; } static FnTableEntry *get_context_fn_entry(BlockContext *context) { assert(context->fn_entry); return context->fn_entry; } static TypeTableEntry *unwrapped_node_type(AstNode *node) { Expr *expr = get_resolved_expr(node); if (expr->type_entry->id == TypeTableEntryIdInvalid) { return expr->type_entry; } assert(expr->type_entry->id == TypeTableEntryIdMetaType); ConstExprValue *const_val = &expr->const_val; assert(const_val->ok); return const_val->data.x_type; } static TypeTableEntry *get_return_type(BlockContext *context) { FnTableEntry *fn_entry = get_context_fn_entry(context); AstNode *fn_proto_node = fn_entry->proto_node; assert(fn_proto_node->type == NodeTypeFnProto); AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; return unwrapped_node_type(return_type_node); } static bool type_has_codegen_value(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: return false; case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: return true; case TypeTableEntryIdTypeDecl: return type_has_codegen_value(type_entry->data.type_decl.canonical_type); } zig_unreachable(); } static void add_global_const_expr(CodeGen *g, AstNode *expr_node) { Expr *expr = get_resolved_expr(expr_node); if (expr->const_val.ok && type_has_codegen_value(expr->type_entry) && !expr->has_global_const && type_has_bits(expr->type_entry)) { g->global_const_list.append(expr_node); expr->has_global_const = true; } } static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTableEntry *other_type) { TypeTableEntry *other_type_underlying = get_underlying_type(other_type); if (other_type_underlying->id == TypeTableEntryIdInvalid) { return false; } Expr *expr = get_resolved_expr(literal_node); ConstExprValue *const_val = &expr->const_val; assert(const_val->ok); if (other_type_underlying->id == TypeTableEntryIdFloat) { return true; } else if (other_type_underlying->id == TypeTableEntryIdInt && const_val->data.x_bignum.kind == BigNumKindInt) { if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type_underlying->data.integral.bit_count, other_type_underlying->data.integral.is_signed)) { return true; } } else if ((other_type_underlying->id == TypeTableEntryIdNumLitFloat && const_val->data.x_bignum.kind == BigNumKindFloat) || (other_type_underlying->id == TypeTableEntryIdNumLitInt && const_val->data.x_bignum.kind == BigNumKindInt)) { return true; } const char *num_lit_str = (const_val->data.x_bignum.kind == BigNumKindFloat) ? "float" : "integer"; add_node_error(g, literal_node, buf_sprintf("%s value %s cannot be implicitly casted to type '%s'", num_lit_str, buf_ptr(bignum_to_buf(&const_val->data.x_bignum)), buf_ptr(&other_type->name))); return false; } static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == actual_type) return true; // pointer const if (expected_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer && (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const)) { return types_match_const_cast_only(expected_type->data.pointer.child_type, actual_type->data.pointer.child_type); } // unknown size array const if (expected_type->id == TypeTableEntryIdStruct && actual_type->id == TypeTableEntryIdStruct && expected_type->data.structure.is_slice && actual_type->data.structure.is_slice && (!actual_type->data.structure.fields[0].type_entry->data.pointer.is_const || expected_type->data.structure.fields[0].type_entry->data.pointer.is_const)) { return types_match_const_cast_only( expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, actual_type->data.structure.fields[0].type_entry->data.pointer.child_type); } // maybe if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { return types_match_const_cast_only( expected_type->data.maybe.child_type, actual_type->data.maybe.child_type); } // error if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { return types_match_const_cast_only( expected_type->data.error.child_type, actual_type->data.error.child_type); } // fn if (expected_type->id == TypeTableEntryIdFn && actual_type->id == TypeTableEntryIdFn) { if (expected_type->data.fn.fn_type_id.is_extern != actual_type->data.fn.fn_type_id.is_extern) { return false; } if (expected_type->data.fn.fn_type_id.is_naked != actual_type->data.fn.fn_type_id.is_naked) { return false; } if (expected_type->data.fn.fn_type_id.is_cold != actual_type->data.fn.fn_type_id.is_cold) { return false; } if (actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable && !types_match_const_cast_only( expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type)) { return false; } if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { return false; } for (int i = 0; i < expected_type->data.fn.fn_type_id.param_count; i += 1) { // note it's reversed for parameters FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) { return false; } if (expected_param_info->is_noalias != actual_param_info->is_noalias) { return false; } } return true; } return false; } static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node, AstNode **child_nodes, TypeTableEntry **child_types, int child_count) { TypeTableEntry *prev_type = child_types[0]; AstNode *prev_node = child_nodes[0]; if (prev_type->id == TypeTableEntryIdInvalid) { return prev_type; } for (int i = 1; i < child_count; i += 1) { TypeTableEntry *cur_type = child_types[i]; AstNode *cur_node = child_nodes[i]; if (cur_type->id == TypeTableEntryIdInvalid) { return cur_type; } else if (types_match_const_cast_only(prev_type, cur_type)) { continue; } else if (types_match_const_cast_only(cur_type, prev_type)) { prev_type = cur_type; prev_node = cur_node; continue; } else if (prev_type->id == TypeTableEntryIdUnreachable) { prev_type = cur_type; prev_node = cur_node; } else if (cur_type->id == TypeTableEntryIdUnreachable) { continue; } else if (prev_type->id == TypeTableEntryIdInt && cur_type->id == TypeTableEntryIdInt && prev_type->data.integral.is_signed == cur_type->data.integral.is_signed && (cur_type->data.integral.bit_count >= prev_type->data.integral.bit_count && (cur_type->data.integral.is_wrapping || !prev_type->data.integral.is_wrapping))) { if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { prev_type = cur_type; prev_node = cur_node; } else if (cur_type->data.integral.is_wrapping && !prev_type->data.integral.is_wrapping) { prev_type = cur_type; prev_node = cur_node; } continue; } else if (prev_type->id == TypeTableEntryIdInt && cur_type->id == TypeTableEntryIdInt && prev_type->data.integral.is_signed == cur_type->data.integral.is_signed && (prev_type->data.integral.bit_count >= cur_type->data.integral.bit_count && (prev_type->data.integral.is_wrapping || !cur_type->data.integral.is_wrapping))) { continue; } else if (prev_type->id == TypeTableEntryIdFloat && cur_type->id == TypeTableEntryIdFloat) { if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { prev_type = cur_type; prev_node = cur_node; } } else if (prev_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(prev_type->data.error.child_type, cur_type)) { continue; } else if (cur_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(cur_type->data.error.child_type, prev_type)) { prev_type = cur_type; prev_node = cur_node; continue; } else if (prev_type->id == TypeTableEntryIdNumLitInt || prev_type->id == TypeTableEntryIdNumLitFloat) { if (num_lit_fits_in_other_type(g, prev_node, cur_type)) { prev_type = cur_type; prev_node = cur_node; continue; } else { return g->builtin_types.entry_invalid; } } else if (cur_type->id == TypeTableEntryIdNumLitInt || cur_type->id == TypeTableEntryIdNumLitFloat) { if (num_lit_fits_in_other_type(g, cur_node, prev_type)) { continue; } else { return g->builtin_types.entry_invalid; } } else { add_node_error(g, parent_source_node, buf_sprintf("incompatible types: '%s' and '%s'", buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); return g->builtin_types.entry_invalid; } } return prev_type; } static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type, AstNode *literal_node, bool *reported_err) { if (types_match_const_cast_only(expected_type, actual_type)) { return true; } // implicit conversion from non maybe type to maybe type if (expected_type->id == TypeTableEntryIdMaybe && types_match_with_implicit_cast(g, expected_type->data.maybe.child_type, actual_type, literal_node, reported_err)) { return true; } // implicit conversion from error child type to error type if (expected_type->id == TypeTableEntryIdErrorUnion && types_match_with_implicit_cast(g, expected_type->data.error.child_type, actual_type, literal_node, reported_err)) { return true; } // implicit conversion from pure error to error union type if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdPureError) { return true; } // implicit widening conversion if (expected_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && expected_type->data.integral.is_signed == actual_type->data.integral.is_signed && expected_type->data.integral.bit_count >= actual_type->data.integral.bit_count) { return true; } // small enough unsigned ints can get casted to large enough signed ints if (expected_type->id == TypeTableEntryIdInt && expected_type->data.integral.is_signed && actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && expected_type->data.integral.bit_count > actual_type->data.integral.bit_count) { return true; } // implicit float widening conversion if (expected_type->id == TypeTableEntryIdFloat && actual_type->id == TypeTableEntryIdFloat && expected_type->data.floating.bit_count >= actual_type->data.floating.bit_count) { return true; } // implicit array to slice conversion if (expected_type->id == TypeTableEntryIdStruct && expected_type->data.structure.is_slice && actual_type->id == TypeTableEntryIdArray && types_match_const_cast_only( expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, actual_type->data.array.child_type)) { return true; } // implicit number literal to typed number if ((actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt)) { if (num_lit_fits_in_other_type(g, literal_node, expected_type)) { return true; } else { *reported_err = true; } } return false; } static AstNode *create_ast_node(CodeGen *g, ImportTableEntry *import, NodeType kind, AstNode *source_node) { AstNode *node = allocate(1); node->type = kind; node->owner = import; node->create_index = g->next_node_index; g->next_node_index += 1; node->line = source_node->line; node->column = source_node->column; return node; } static AstNode *create_ast_type_node(CodeGen *g, ImportTableEntry *import, TypeTableEntry *type_entry, AstNode *source_node) { AstNode *node = create_ast_node(g, import, NodeTypeSymbol, source_node); node->data.symbol_expr.override_type_entry = type_entry; return node; } static AstNode *create_ast_void_node(CodeGen *g, ImportTableEntry *import, AstNode *source_node) { AstNode *node = create_ast_node(g, import, NodeTypeContainerInitExpr, source_node); node->data.container_init_expr.kind = ContainerInitKindArray; node->data.container_init_expr.type = create_ast_type_node(g, import, g->builtin_types.entry_void, source_node); normalize_parent_ptrs(node); return node; } static TypeTableEntry *create_and_analyze_cast_node(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *cast_to_type, AstNode *node) { AstNode *new_parent_node = create_ast_node(g, import, NodeTypeFnCallExpr, node); *node->parent_field = new_parent_node; new_parent_node->parent_field = node->parent_field; new_parent_node->data.fn_call_expr.fn_ref_expr = create_ast_type_node(g, import, cast_to_type, node); new_parent_node->data.fn_call_expr.params.append(node); normalize_parent_ptrs(new_parent_node); return analyze_expression(g, import, context, cast_to_type, new_parent_node); } static TypeTableEntry *resolve_type_compatibility(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == nullptr) return actual_type; // anything will do if (expected_type == actual_type) return expected_type; // match if (expected_type->id == TypeTableEntryIdInvalid || actual_type->id == TypeTableEntryIdInvalid) return g->builtin_types.entry_invalid; if (actual_type->id == TypeTableEntryIdUnreachable) return actual_type; bool reported_err = false; if (types_match_with_implicit_cast(g, expected_type, actual_type, node, &reported_err)) { return create_and_analyze_cast_node(g, import, context, expected_type, node); } if (!reported_err) { add_node_error(g, first_executing_node(node), buf_sprintf("expected type '%s', got '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); } return g->builtin_types.entry_invalid; } static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, AstNode *parent_source_node, AstNode **child_nodes, TypeTableEntry **child_types, int child_count) { assert(child_count > 0); TypeTableEntry *expected_type = determine_peer_type_compatibility(g, parent_source_node, child_nodes, child_types, child_count); if (expected_type->id == TypeTableEntryIdInvalid) { return expected_type; } for (int i = 0; i < child_count; i += 1) { if (!child_nodes[i]) { continue; } AstNode **child_node = child_nodes[i]->parent_field; TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, block_context, *child_node, expected_type, child_types[i]); Expr *expr = get_resolved_expr(*child_node); expr->type_entry = resolved_type; add_global_const_expr(g, *child_node); } return expected_type; } BlockContext *new_block_context(AstNode *node, BlockContext *parent) { BlockContext *context = allocate(1); context->node = node; context->parent = parent; context->decl_table.init(1); context->var_table.init(1); context->label_table.init(1); if (parent) { context->parent_loop_node = parent->parent_loop_node; context->c_import_buf = parent->c_import_buf; context->codegen_excluded = parent->codegen_excluded; context->safety_off = parent->safety_off; } if (node && node->type == NodeTypeFnDef) { AstNode *fn_proto_node = node->data.fn_def.fn_proto; context->fn_entry = fn_proto_node->data.fn_proto.fn_table_entry; context->safety_off = context->fn_entry->safety_off; } else if (parent) { context->fn_entry = parent->fn_entry; } if (context->fn_entry) { context->fn_entry->all_block_contexts.append(context); } return context; } static AstNode *find_decl(BlockContext *context, Buf *name) { while (context) { auto entry = context->decl_table.maybe_get(name); if (entry) { return entry->value; } context = context->parent; } return nullptr; } static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name) { BlockContext *context = orig_context; while (context) { auto entry = context->var_table.maybe_get(name); if (entry) { return entry->value; } context = context->parent; } return nullptr; } static LabelTableEntry *find_label(CodeGen *g, BlockContext *orig_context, Buf *name) { BlockContext *context = orig_context; while (context && context->fn_entry) { auto entry = context->label_table.maybe_get(name); if (entry) { return entry->value; } context = context->parent; } return nullptr; } static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) { for (uint32_t i = 0; i < enum_type->data.enumeration.field_count; i += 1) { TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; if (buf_eql_buf(type_enum_field->name, name)) { return type_enum_field; } } return nullptr; } static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *field_access_node, AstNode *value_node, TypeTableEntry *enum_type, Buf *field_name, AstNode *out_node) { assert(field_access_node->type == NodeTypeFieldAccessExpr); TypeEnumField *type_enum_field = get_enum_field(enum_type, field_name); field_access_node->data.field_access_expr.type_enum_field = type_enum_field; if (type_enum_field) { if (value_node) { AstNode **value_node_ptr = value_node->parent_field; TypeTableEntry *value_type = analyze_expression(g, import, context, type_enum_field->type_entry, value_node); if (value_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } StructValExprCodeGen *codegen = &field_access_node->data.field_access_expr.resolved_struct_val_expr; codegen->type_entry = enum_type; codegen->source_node = field_access_node; ConstExprValue *value_const_val = &get_resolved_expr(*value_node_ptr)->const_val; if (value_const_val->ok) { ConstExprValue *const_val = &get_resolved_expr(out_node)->const_val; const_val->ok = true; const_val->data.x_enum.tag = type_enum_field->value; const_val->data.x_enum.payload = value_const_val; } else { if (context->fn_entry) { context->fn_entry->struct_val_expr_alloca_list.append(codegen); } else { add_node_error(g, *value_node_ptr, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } } } else if (type_enum_field->type_entry->id != TypeTableEntryIdVoid) { add_node_error(g, field_access_node, buf_sprintf("enum value '%s.%s' requires parameter of type '%s'", buf_ptr(&enum_type->name), buf_ptr(field_name), buf_ptr(&type_enum_field->type_entry->name))); } else { Expr *expr = get_resolved_expr(out_node); expr->const_val.ok = true; expr->const_val.data.x_enum.tag = type_enum_field->value; expr->const_val.data.x_enum.payload = nullptr; } } else { add_node_error(g, field_access_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&enum_type->name))); } return enum_type; } static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->id == TypeTableEntryIdStruct); assert(type_entry->data.structure.complete); for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { TypeStructField *field = &type_entry->data.structure.fields[i]; if (buf_eql_buf(field->name, name)) { return field; } } return nullptr; } static const char *err_container_init_syntax_name(ContainerInitKind kind) { switch (kind) { case ContainerInitKindStruct: return "struct"; case ContainerInitKindArray: return "array"; } zig_unreachable(); } static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeContainerInitExpr); AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; if (container_init_expr->type->type == NodeTypeFieldAccessExpr) { container_init_expr->type->data.field_access_expr.container_init_expr_node = node; } TypeTableEntry *container_meta_type = analyze_expression(g, import, context, nullptr, container_init_expr->type); if (container_meta_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } if (node->data.container_init_expr.enum_type) { get_resolved_expr(node)->const_val = get_resolved_expr(container_init_expr->type)->const_val; return node->data.container_init_expr.enum_type; } TypeTableEntry *container_type = resolve_type(g, container_init_expr->type); if (container_type->id == TypeTableEntryIdInvalid) { return container_type; } else if (container_type->id == TypeTableEntryIdStruct && !container_type->data.structure.is_slice && (kind == ContainerInitKindStruct || (kind == ContainerInitKindArray && container_init_expr->entries.length == 0))) { StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; codegen->type_entry = container_type; codegen->source_node = node; int expr_field_count = container_init_expr->entries.length; int actual_field_count = container_type->data.structure.src_field_count; AstNode *non_const_expr_culprit = nullptr; int *field_use_counts = allocate(actual_field_count); ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; const_val->data.x_struct.fields = allocate(actual_field_count); for (int i = 0; i < expr_field_count; i += 1) { AstNode *val_field_node = container_init_expr->entries.at(i); assert(val_field_node->type == NodeTypeStructValueField); val_field_node->block_context = context; TypeStructField *type_field = find_struct_type_field(container_type, &val_field_node->data.struct_val_field.name); if (!type_field) { add_node_error(g, val_field_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(&val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name))); continue; } if (type_field->type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } int field_index = type_field->src_index; field_use_counts[field_index] += 1; if (field_use_counts[field_index] > 1) { add_node_error(g, val_field_node, buf_sprintf("duplicate field")); continue; } val_field_node->data.struct_val_field.type_struct_field = type_field; analyze_expression(g, import, context, type_field->type_entry, val_field_node->data.struct_val_field.expr); if (const_val->ok) { ConstExprValue *field_val = &get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val; if (field_val->ok) { const_val->data.x_struct.fields[field_index] = field_val; const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var; } else { const_val->ok = false; non_const_expr_culprit = val_field_node->data.struct_val_field.expr; } } } if (!const_val->ok) { assert(non_const_expr_culprit); if (context->fn_entry) { context->fn_entry->struct_val_expr_alloca_list.append(codegen); } else { add_node_error(g, non_const_expr_culprit, buf_sprintf("unable to evaluate constant expression")); } } for (int i = 0; i < actual_field_count; i += 1) { if (field_use_counts[i] == 0) { add_node_error(g, node, buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); } } return container_type; } else if (container_type->id == TypeTableEntryIdStruct && container_type->data.structure.is_slice && kind == ContainerInitKindArray) { int elem_count = container_init_expr->entries.length; TypeTableEntry *pointer_type = container_type->data.structure.fields[0].type_entry; assert(pointer_type->id == TypeTableEntryIdPointer); TypeTableEntry *child_type = pointer_type->data.pointer.child_type; ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; const_val->data.x_array.fields = allocate(elem_count); for (int i = 0; i < elem_count; i += 1) { AstNode **elem_node = &container_init_expr->entries.at(i); analyze_expression(g, import, context, child_type, *elem_node); if (const_val->ok) { ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val; if (elem_const_val->ok) { const_val->data.x_array.fields[i] = elem_const_val; const_val->depends_on_compile_var = const_val->depends_on_compile_var || elem_const_val->depends_on_compile_var; } else { const_val->ok = false; } } } TypeTableEntry *fixed_size_array_type = get_array_type(g, child_type, elem_count); StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; codegen->type_entry = fixed_size_array_type; codegen->source_node = node; if (!const_val->ok) { context->fn_entry->struct_val_expr_alloca_list.append(codegen); } return fixed_size_array_type; } else if (container_type->id == TypeTableEntryIdArray) { zig_panic("TODO array container init"); return container_type; } else if (container_type->id == TypeTableEntryIdVoid) { if (container_init_expr->entries.length != 0) { add_node_error(g, node, buf_sprintf("void expression expects no arguments")); return g->builtin_types.entry_invalid; } else { return resolve_expr_const_val_as_void(g, node); } } else if (container_type->id == TypeTableEntryIdUnreachable) { if (container_init_expr->entries.length != 0) { add_node_error(g, node, buf_sprintf("unreachable expression expects no arguments")); return g->builtin_types.entry_invalid; } else { return container_type; } } else { add_node_error(g, node, buf_sprintf("type '%s' does not support %s initialization syntax", buf_ptr(&container_type->name), err_container_init_syntax_name(kind))); return g->builtin_types.entry_invalid; } } static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeFieldAccessExpr); AstNode **struct_expr_node = &node->data.field_access_expr.struct_expr; TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node); Buf *field_name = &node->data.field_access_expr.field_name; bool wrapped_in_fn_call = node->data.field_access_expr.is_fn_call; if (struct_type->id == TypeTableEntryIdInvalid) { return struct_type; } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer && struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) { TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ? struct_type : struct_type->data.pointer.child_type; if (!bare_struct_type->data.structure.complete) { resolve_struct_type(g, bare_struct_type->data.structure.decl_node->owner, bare_struct_type); } node->data.field_access_expr.bare_struct_type = bare_struct_type; node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_struct_type, field_name); if (node->data.field_access_expr.type_struct_field) { return node->data.field_access_expr.type_struct_field->type_entry; } else if (wrapped_in_fn_call) { BlockContext *container_block_context = get_container_block_context(bare_struct_type); auto entry = container_block_context->decl_table.maybe_get(field_name); AstNode *fn_decl_node = entry ? entry->value : nullptr; if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) { resolve_top_level_decl(g, fn_decl_node, false); TopLevelDecl *tld = get_as_top_level_decl(fn_decl_node); if (tld->resolution == TldResolutionInvalid) { return g->builtin_types.entry_invalid; } node->data.field_access_expr.is_member_fn = true; FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry; if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) { return resolve_expr_const_val_as_generic_fn(g, node, fn_entry->type_entry, false); } else { return resolve_expr_const_val_as_fn(g, node, fn_entry, false); } } else { add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); return g->builtin_types.entry_invalid; } } else { add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); return g->builtin_types.entry_invalid; } } else if (struct_type->id == TypeTableEntryIdArray) { if (buf_eql_str(field_name, "len")) { return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, struct_type->data.array.len, false); } else { add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); return g->builtin_types.entry_invalid; } } else if (struct_type->id == TypeTableEntryIdMetaType) { TypeTableEntry *child_type = resolve_type(g, *struct_expr_node); if (child_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (child_type->id == TypeTableEntryIdEnum) { AstNode *container_init_node = node->data.field_access_expr.container_init_expr_node; AstNode *value_node; if (container_init_node) { assert(container_init_node->type == NodeTypeContainerInitExpr); int param_count = container_init_node->data.container_init_expr.entries.length; if (param_count > 1) { AstNode *first_invalid_node = container_init_node->data.container_init_expr.entries.at(1); add_node_error(g, first_executing_node(first_invalid_node), buf_sprintf("enum values accept only one parameter")); return child_type; } else { if (param_count == 1) { value_node = container_init_node->data.container_init_expr.entries.at(0); } else { value_node = nullptr; } container_init_node->data.container_init_expr.enum_type = child_type; } } else { value_node = nullptr; } return analyze_enum_value_expr(g, import, context, node, value_node, child_type, field_name, node); } else if (child_type->id == TypeTableEntryIdStruct) { BlockContext *container_block_context = get_container_block_context(child_type); auto entry = container_block_context->decl_table.maybe_get(field_name); AstNode *decl_node = entry ? entry->value : nullptr; if (decl_node) { bool pointer_only = false; return analyze_decl_ref(g, node, decl_node, pointer_only, context, false); } else { add_node_error(g, node, buf_sprintf("container '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return g->builtin_types.entry_invalid; } } else if (child_type->id == TypeTableEntryIdPureError) { return analyze_error_literal_expr(g, import, context, node, field_name); } else if (child_type->id == TypeTableEntryIdInt) { bool depends_on_compile_var = get_resolved_expr(*struct_expr_node)->const_val.depends_on_compile_var; if (buf_eql_str(field_name, "bit_count")) { return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, child_type->data.integral.bit_count, depends_on_compile_var); } else if (buf_eql_str(field_name, "is_signed")) { return resolve_expr_const_val_as_bool(g, node, child_type->data.integral.is_signed, depends_on_compile_var); } else if (buf_eql_str(field_name, "is_wrapping")) { return resolve_expr_const_val_as_bool(g, node, child_type->data.integral.is_wrapping, depends_on_compile_var); } else { add_node_error(g, node, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return g->builtin_types.entry_invalid; } } else if (wrapped_in_fn_call) { // this branch should go last, before the error in the else case return resolve_expr_const_val_as_type(g, node, child_type, false); } else { add_node_error(g, node, buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name))); return g->builtin_types.entry_invalid; } } else if (struct_type->id == TypeTableEntryIdNamespace) { ConstExprValue *const_val = &get_resolved_expr(*struct_expr_node)->const_val; assert(const_val->ok); ImportTableEntry *namespace_import = const_val->data.x_import; AstNode *decl_node = find_decl(namespace_import->block_context, field_name); if (!decl_node) { // we must now resolve all the use decls for (int i = 0; i < namespace_import->use_decls.length; i += 1) { AstNode *use_decl_node = namespace_import->use_decls.at(i); if (!get_resolved_expr(use_decl_node->data.use.expr)->type_entry) { preview_use_decl(g, use_decl_node); } resolve_use_decl(g, use_decl_node); } decl_node = find_decl(namespace_import->block_context, field_name); } if (decl_node) { TopLevelDecl *tld = get_as_top_level_decl(decl_node); if (tld->visib_mod == VisibModPrivate) { ErrorMsg *msg = add_node_error(g, node, buf_sprintf("'%s' is private", buf_ptr(field_name))); add_error_note(g, msg, decl_node, buf_sprintf("declared here")); } bool pointer_only = false; return analyze_decl_ref(g, node, decl_node, pointer_only, context, const_val->depends_on_compile_var); } else { const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)"; add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name)); return g->builtin_types.entry_invalid; } } else { add_node_error(g, node, buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name))); return g->builtin_types.entry_invalid; } } static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeSliceExpr); TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, node->data.slice_expr.array_ref_expr); TypeTableEntry *return_type; if (array_type->id == TypeTableEntryIdInvalid) { return_type = g->builtin_types.entry_invalid; } else if (array_type->id == TypeTableEntryIdArray) { return_type = get_slice_type(g, array_type->data.array.child_type, node->data.slice_expr.is_const); } else if (array_type->id == TypeTableEntryIdPointer) { return_type = get_slice_type(g, array_type->data.pointer.child_type, node->data.slice_expr.is_const); } else if (array_type->id == TypeTableEntryIdStruct && array_type->data.structure.is_slice) { return_type = get_slice_type(g, array_type->data.structure.fields[0].type_entry->data.pointer.child_type, node->data.slice_expr.is_const); } else { add_node_error(g, node, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); return_type = g->builtin_types.entry_invalid; } if (return_type->id != TypeTableEntryIdInvalid) { node->data.slice_expr.resolved_struct_val_expr.type_entry = return_type; node->data.slice_expr.resolved_struct_val_expr.source_node = node; context->fn_entry->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr); } analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.start); if (node->data.slice_expr.end) { analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.end); } return return_type; } static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, node->data.array_access_expr.array_ref_expr); TypeTableEntry *return_type; if (array_type->id == TypeTableEntryIdInvalid) { return_type = g->builtin_types.entry_invalid; } else if (array_type->id == TypeTableEntryIdArray) { if (array_type->data.array.len == 0) { add_node_error(g, node, buf_sprintf("out of bounds array access")); } return_type = array_type->data.array.child_type; } else if (array_type->id == TypeTableEntryIdPointer) { return_type = array_type->data.pointer.child_type; } else if (array_type->id == TypeTableEntryIdStruct && array_type->data.structure.is_slice) { return_type = array_type->data.structure.fields[0].type_entry->data.pointer.child_type; } else { add_node_error(g, node, buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); return_type = g->builtin_types.entry_invalid; } analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.array_access_expr.subscript); return return_type; } static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; return g->builtin_types.entry_void; } static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type, bool depends_on_compile_var) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_type = type; expr->const_val.depends_on_compile_var = depends_on_compile_var; return g->builtin_types.entry_type; } static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode *node, AstNode *other, bool depends_on_compile_var) { Expr *expr = get_resolved_expr(node); Expr *other_expr = get_resolved_expr(other); expr->const_val = other_expr->const_val; expr->const_val.depends_on_compile_var = expr->const_val.depends_on_compile_var || depends_on_compile_var; return other_expr->type_entry; } static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, bool depends_on_compile_var) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_fn = fn; expr->const_val.depends_on_compile_var = depends_on_compile_var; return fn->type_entry; } static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node, TypeTableEntry *type_entry, bool depends_on_compile_var) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_type = type_entry; expr->const_val.depends_on_compile_var = depends_on_compile_var; return type_entry; } static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, ErrorTableEntry *err) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_err.err = err; return g->builtin_types.entry_pure_error; } static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value, bool depends_on_compile_var) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.depends_on_compile_var = depends_on_compile_var; expr->const_val.data.x_bool = value; return g->builtin_types.entry_bool; } static TypeTableEntry *resolve_expr_const_val_as_null(CodeGen *g, AstNode *node, TypeTableEntry *type) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_maybe = nullptr; return type; } static TypeTableEntry *resolve_expr_const_val_as_non_null(CodeGen *g, AstNode *node, TypeTableEntry *type, ConstExprValue *other_val) { assert(other_val->ok); Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_maybe = other_val; return type; } static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNode *node, Buf *str) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; int len_with_null = buf_len(str) + 1; expr->const_val.data.x_ptr.ptr = allocate(len_with_null); expr->const_val.data.x_ptr.len = len_with_null; expr->const_val.data.x_ptr.is_c_str = true; ConstExprValue *all_chars = allocate(len_with_null); for (int i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &all_chars[i]; this_char->ok = true; bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]); expr->const_val.data.x_ptr.ptr[i] = this_char; } ConstExprValue *null_char = &all_chars[len_with_null - 1]; null_char->ok = true; bignum_init_unsigned(&null_char->data.x_bignum, 0); expr->const_val.data.x_ptr.ptr[len_with_null - 1] = null_char; return get_pointer_to_type(g, g->builtin_types.entry_u8, true); } static TypeTableEntry *resolve_expr_const_val_as_string_lit(CodeGen *g, AstNode *node, Buf *str) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_array.fields = allocate(buf_len(str)); ConstExprValue *all_chars = allocate(buf_len(str)); for (int i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &all_chars[i]; this_char->ok = true; bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]); expr->const_val.data.x_array.fields[i] = this_char; } return get_array_type(g, g->builtin_types.entry_u8, buf_len(str)); } static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node, TypeTableEntry *expected_type, uint64_t x, bool depends_on_compile_var) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.depends_on_compile_var = depends_on_compile_var; bignum_init_unsigned(&expr->const_val.data.x_bignum, x); return g->builtin_types.entry_num_lit_int; } static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNode *node, TypeTableEntry *expected_type, double x) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; bignum_init_float(&expr->const_val.data.x_bignum, x); return g->builtin_types.entry_num_lit_float; } static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, Buf *err_name) { auto err_table_entry = g->error_table.maybe_get(err_name); if (err_table_entry) { return resolve_expr_const_val_as_err(g, node, err_table_entry->value); } add_node_error(g, node, buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name))); return g->builtin_types.entry_invalid; } static bool var_is_pure(VariableTableEntry *var, BlockContext *context) { if (var->block_context->fn_entry == context->fn_entry) { // variable was declared in the current function, so it's OK. return true; } return var->is_const && var->type->deep_const; } static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var, BlockContext *context, bool depends_on_compile_var) { get_resolved_expr(source_node)->variable = var; if (!var_is_pure(var, context)) { mark_impure_fn(context); } if (var->is_const && var->val_node) { ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val; if (other_const_val->ok) { return resolve_expr_const_val_as_other_expr(g, source_node, var->val_node, depends_on_compile_var || var->force_depends_on_compile_var); } } return var->type; } static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only, BlockContext *block_context, bool depends_on_compile_var) { resolve_top_level_decl(g, decl_node, pointer_only); TopLevelDecl *tld = get_as_top_level_decl(decl_node); if (tld->resolution == TldResolutionInvalid) { return g->builtin_types.entry_invalid; } if (decl_node->type == NodeTypeVariableDeclaration) { VariableTableEntry *var = decl_node->data.variable_declaration.variable; return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var); } else if (decl_node->type == NodeTypeFnProto) { FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; assert(fn_entry->type_entry); if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) { return resolve_expr_const_val_as_generic_fn(g, source_node, fn_entry->type_entry, depends_on_compile_var); } else { return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var); } } else if (decl_node->type == NodeTypeContainerDecl) { if (decl_node->data.struct_decl.generic_params.length > 0) { TypeTableEntry *type_entry = decl_node->data.struct_decl.generic_fn_type; assert(type_entry); return resolve_expr_const_val_as_generic_fn(g, source_node, type_entry, depends_on_compile_var); } else { return resolve_expr_const_val_as_type(g, source_node, decl_node->data.struct_decl.type_entry, depends_on_compile_var); } } else if (decl_node->type == NodeTypeTypeDecl) { return resolve_expr_const_val_as_type(g, source_node, decl_node->data.type_decl.child_type_entry, depends_on_compile_var); } else { zig_unreachable(); } } static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { if (node->data.symbol_expr.override_type_entry) { return resolve_expr_const_val_as_type(g, node, node->data.symbol_expr.override_type_entry, false); } Buf *variable_name = &node->data.symbol_expr.symbol; auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name); if (primitive_table_entry) { return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value, false); } VariableTableEntry *var = find_variable(g, context, variable_name); if (var) { TypeTableEntry *var_type = analyze_var_ref(g, node, var, context, false); return var_type; } AstNode *decl_node = find_decl(context, variable_name); if (decl_node) { return analyze_decl_ref(g, node, decl_node, pointer_only, context, false); } if (import->any_imports_failed) { // skip the error message since we had a failing import in this file // if an import breaks we don't need 9999 undeclared identifier errors return g->builtin_types.entry_invalid; } mark_impure_fn(context); add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); return g->builtin_types.entry_invalid; } static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { switch (op) { case BinOpTypeAssign: return true; case BinOpTypeAssignTimes: case BinOpTypeAssignDiv: case BinOpTypeAssignMod: return type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdFloat; case BinOpTypeAssignPlus: case BinOpTypeAssignMinus: return type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdFloat || type->id == TypeTableEntryIdPointer; case BinOpTypeAssignBitShiftLeft: case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitXor: case BinOpTypeAssignBitOr: return type->id == TypeTableEntryIdInt; case BinOpTypeAssignBoolAnd: case BinOpTypeAssignBoolOr: return type->id == TypeTableEntryIdBool; case BinOpTypeInvalid: case BinOpTypeBoolOr: case BinOpTypeBoolAnd: case BinOpTypeCmpEq: case BinOpTypeCmpNotEq: case BinOpTypeCmpLessThan: case BinOpTypeCmpGreaterThan: case BinOpTypeCmpLessOrEq: case BinOpTypeCmpGreaterOrEq: case BinOpTypeBinOr: case BinOpTypeBinXor: case BinOpTypeBinAnd: case BinOpTypeBitShiftLeft: case BinOpTypeBitShiftRight: case BinOpTypeAdd: case BinOpTypeSub: case BinOpTypeMult: case BinOpTypeDiv: case BinOpTypeMod: case BinOpTypeUnwrapMaybe: case BinOpTypeArrayCat: case BinOpTypeArrayMult: zig_unreachable(); } zig_unreachable(); } enum LValPurpose { LValPurposeAssign, LValPurposeAddressOf, }; static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, AstNode *lhs_node, LValPurpose purpose, bool is_ptr_const) { TypeTableEntry *expected_rhs_type = nullptr; lhs_node->block_context = block_context; if (lhs_node->type == NodeTypeSymbol) { bool pointer_only = purpose == LValPurposeAddressOf; expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node, pointer_only); if (expected_rhs_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } if (purpose != LValPurposeAddressOf) { Buf *name = &lhs_node->data.symbol_expr.symbol; VariableTableEntry *var = find_variable(g, block_context, name); if (var) { if (var->is_const) { add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant")); expected_rhs_type = g->builtin_types.entry_invalid; } else { expected_rhs_type = var->type; get_resolved_expr(lhs_node)->variable = var; } } else { add_node_error(g, lhs_node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); expected_rhs_type = g->builtin_types.entry_invalid; } } } else if (lhs_node->type == NodeTypeArrayAccessExpr) { expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node); } else if (lhs_node->type == NodeTypeFieldAccessExpr) { expected_rhs_type = analyze_field_access_expr(g, import, block_context, nullptr, lhs_node); } else if (lhs_node->type == NodeTypePrefixOpExpr && lhs_node->data.prefix_op_expr.prefix_op == PrefixOpDereference) { assert(purpose == LValPurposeAssign); AstNode *target_node = lhs_node->data.prefix_op_expr.primary_expr; TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, target_node); if (type_entry->id == TypeTableEntryIdInvalid) { expected_rhs_type = type_entry; } else if (type_entry->id == TypeTableEntryIdPointer) { expected_rhs_type = type_entry->data.pointer.child_type; } else { add_node_error(g, target_node, buf_sprintf("indirection requires pointer operand ('%s' invalid)", buf_ptr(&type_entry->name))); expected_rhs_type = g->builtin_types.entry_invalid; } } else { if (purpose == LValPurposeAssign) { add_node_error(g, lhs_node, buf_sprintf("invalid assignment target")); expected_rhs_type = g->builtin_types.entry_invalid; } else if (purpose == LValPurposeAddressOf) { TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, lhs_node); if (type_entry->id == TypeTableEntryIdInvalid) { expected_rhs_type = g->builtin_types.entry_invalid; } else if (type_entry->id == TypeTableEntryIdMetaType) { expected_rhs_type = type_entry; } else { add_node_error(g, lhs_node, buf_sprintf("invalid addressof target")); expected_rhs_type = g->builtin_types.entry_invalid; } } } assert(expected_rhs_type); return expected_rhs_type; } static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; AstNode **op1 = &node->data.bin_op_expr.op1; AstNode **op2 = &node->data.bin_op_expr.op2; TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1); TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2); AstNode *op_nodes[] = {*op1, *op2}; TypeTableEntry *op_types[] = {op1_type, op2_type}; TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2); bool is_equality_cmp = (bin_op_type == BinOpTypeCmpEq || bin_op_type == BinOpTypeCmpNotEq); switch (resolved_type->id) { case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: break; case TypeTableEntryIdBool: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdPointer: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: if (!is_equality_cmp) { add_node_error(g, node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return g->builtin_types.entry_invalid; } break; case TypeTableEntryIdEnum: if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) { add_node_error(g, node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return g->builtin_types.entry_invalid; } break; case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdUnion: add_node_error(g, node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return g->builtin_types.entry_invalid; } ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val; ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; if (!op1_val->ok || !op2_val->ok) { return g->builtin_types.entry_bool; } ConstExprValue *out_val = &get_resolved_expr(node)->const_val; eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val); return g->builtin_types.entry_bool; } static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; AstNode *op1 = node->data.bin_op_expr.op1; AstNode *op2 = node->data.bin_op_expr.op2; TypeTableEntry *op1_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op1); TypeTableEntry *op2_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op2); if (op1_type->id == TypeTableEntryIdInvalid || op2_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val; ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val; if (!op1_val->ok || !op2_val->ok) { return g->builtin_types.entry_bool; } ConstExprValue *out_val = &get_resolved_expr(node)->const_val; eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val); return g->builtin_types.entry_bool; } static TypeTableEntry *analyze_array_mult(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); assert(node->data.bin_op_expr.bin_op == BinOpTypeArrayMult); AstNode **op1 = node->data.bin_op_expr.op1->parent_field; AstNode **op2 = node->data.bin_op_expr.op2->parent_field; TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1); TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2); if (op1_type->id == TypeTableEntryIdInvalid || op2_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val; ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; AstNode *bad_node; if (!op1_val->ok) { bad_node = *op1; } else if (!op2_val->ok) { bad_node = *op2; } else { bad_node = nullptr; } if (bad_node) { add_node_error(g, bad_node, buf_sprintf("array multiplication requires constant expression")); return g->builtin_types.entry_invalid; } if (op1_type->id != TypeTableEntryIdArray) { add_node_error(g, *op1, buf_sprintf("expected array type, got '%s'", buf_ptr(&op1_type->name))); return g->builtin_types.entry_invalid; } if (op2_type->id != TypeTableEntryIdNumLitInt && op2_type->id != TypeTableEntryIdInt) { add_node_error(g, *op2, buf_sprintf("expected integer type, got '%s'", buf_ptr(&op2_type->name))); return g->builtin_types.entry_invalid; } if (op2_val->data.x_bignum.is_negative) { add_node_error(g, *op2, buf_sprintf("expected positive number")); return g->builtin_types.entry_invalid; } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; const_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; TypeTableEntry *child_type = op1_type->data.array.child_type; BigNum old_array_len; bignum_init_unsigned(&old_array_len, op1_type->data.array.len); BigNum new_array_len; if (bignum_mul(&new_array_len, &old_array_len, &op2_val->data.x_bignum)) { add_node_error(g, node, buf_sprintf("operation results in overflow")); return g->builtin_types.entry_invalid; } uint64_t old_array_len_bare = op1_type->data.array.len; uint64_t operand_amt = op2_val->data.x_bignum.data.x_uint; uint64_t new_array_len_bare = new_array_len.data.x_uint; const_val->data.x_array.fields = allocate(new_array_len_bare); uint64_t i = 0; for (uint64_t x = 0; x < operand_amt; x += 1) { for (uint64_t y = 0; y < old_array_len_bare; y += 1) { const_val->data.x_array.fields[i] = op1_val->data.x_array.fields[y]; i += 1; } } return get_array_type(g, child_type, new_array_len_bare); } static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; switch (bin_op_type) { case BinOpTypeAssign: case BinOpTypeAssignTimes: case BinOpTypeAssignDiv: case BinOpTypeAssignMod: case BinOpTypeAssignPlus: case BinOpTypeAssignMinus: case BinOpTypeAssignBitShiftLeft: case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitXor: case BinOpTypeAssignBitOr: case BinOpTypeAssignBoolAnd: case BinOpTypeAssignBoolOr: { AstNode *lhs_node = node->data.bin_op_expr.op1; TypeTableEntry *expected_rhs_type = analyze_lvalue(g, import, context, lhs_node, LValPurposeAssign, false); if (expected_rhs_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (!is_op_allowed(expected_rhs_type, node->data.bin_op_expr.bin_op)) { if (expected_rhs_type->id != TypeTableEntryIdInvalid) { add_node_error(g, lhs_node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&expected_rhs_type->name))); } } analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); // not const ok because expression has side effects return g->builtin_types.entry_void; } case BinOpTypeBoolOr: case BinOpTypeBoolAnd: return analyze_logic_bin_op_expr(g, import, context, node); case BinOpTypeCmpEq: case BinOpTypeCmpNotEq: case BinOpTypeCmpLessThan: case BinOpTypeCmpGreaterThan: case BinOpTypeCmpLessOrEq: case BinOpTypeCmpGreaterOrEq: return analyze_bool_bin_op_expr(g, import, context, node); case BinOpTypeBinOr: case BinOpTypeBinXor: case BinOpTypeBinAnd: case BinOpTypeBitShiftLeft: case BinOpTypeBitShiftRight: case BinOpTypeAdd: case BinOpTypeSub: case BinOpTypeMult: case BinOpTypeDiv: case BinOpTypeMod: { AstNode **op1 = node->data.bin_op_expr.op1->parent_field; AstNode **op2 = node->data.bin_op_expr.op2->parent_field; TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, *op1); TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, *op2); AstNode *op_nodes[] = {*op1, *op2}; TypeTableEntry *op_types[] = {lhs_type, rhs_type}; TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2); if (resolved_type->id == TypeTableEntryIdInvalid) { return resolved_type; } if (resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt) { // int } else if ((resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdNumLitFloat) && (bin_op_type == BinOpTypeAdd || bin_op_type == BinOpTypeSub || bin_op_type == BinOpTypeMult || bin_op_type == BinOpTypeDiv || bin_op_type == BinOpTypeMod)) { // float } else { add_node_error(g, node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); return g->builtin_types.entry_invalid; } ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val; ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; if (!op1_val->ok || !op2_val->ok) { return resolved_type; } ConstExprValue *out_val = &get_resolved_expr(node)->const_val; int err; if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type, op2_val, resolved_type, out_val))) { if (err == ErrorDivByZero) { add_node_error(g, node, buf_sprintf("division by zero is undefined")); return g->builtin_types.entry_invalid; } else if (err == ErrorOverflow) { add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type")); return g->builtin_types.entry_invalid; } return g->builtin_types.entry_invalid; } num_lit_fits_in_other_type(g, node, resolved_type); return resolved_type; } case BinOpTypeUnwrapMaybe: { AstNode *op1 = node->data.bin_op_expr.op1; AstNode *op2 = node->data.bin_op_expr.op2; TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1); if (lhs_type->id == TypeTableEntryIdInvalid) { return lhs_type; } else if (lhs_type->id == TypeTableEntryIdMaybe) { TypeTableEntry *child_type = lhs_type->data.maybe.child_type; analyze_expression(g, import, context, child_type, op2); return child_type; } else { add_node_error(g, op1, buf_sprintf("expected maybe type, got '%s'", buf_ptr(&lhs_type->name))); return g->builtin_types.entry_invalid; } } case BinOpTypeArrayCat: { AstNode **op1 = node->data.bin_op_expr.op1->parent_field; AstNode **op2 = node->data.bin_op_expr.op2->parent_field; TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1); TypeTableEntry *child_type; if (op1_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (op1_type->id == TypeTableEntryIdArray) { child_type = op1_type->data.array.child_type; } else if (op1_type->id == TypeTableEntryIdPointer && op1_type->data.pointer.child_type == g->builtin_types.entry_u8) { child_type = op1_type->data.pointer.child_type; } else { add_node_error(g, *op1, buf_sprintf("expected array or C string literal, got '%s'", buf_ptr(&op1_type->name))); return g->builtin_types.entry_invalid; } TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2); if (op2_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (op2_type->id == TypeTableEntryIdArray) { if (op2_type->data.array.child_type != child_type) { add_node_error(g, *op2, buf_sprintf("expected array of type '%s', got '%s'", buf_ptr(&child_type->name), buf_ptr(&op2_type->name))); return g->builtin_types.entry_invalid; } } else if (op2_type->id == TypeTableEntryIdPointer && op2_type->data.pointer.child_type == g->builtin_types.entry_u8) { } else { add_node_error(g, *op2, buf_sprintf("expected array or C string literal, got '%s'", buf_ptr(&op2_type->name))); return g->builtin_types.entry_invalid; } ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val; ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; AstNode *bad_node; if (!op1_val->ok) { bad_node = *op1; } else if (!op2_val->ok) { bad_node = *op2; } else { bad_node = nullptr; } if (bad_node) { add_node_error(g, bad_node, buf_sprintf("array concatenation requires constant expression")); return g->builtin_types.entry_invalid; } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; const_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; if (op1_type->id == TypeTableEntryIdArray) { uint64_t new_len = op1_type->data.array.len + op2_type->data.array.len; const_val->data.x_array.fields = allocate(new_len); uint64_t next_index = 0; for (uint64_t i = 0; i < op1_type->data.array.len; i += 1, next_index += 1) { const_val->data.x_array.fields[next_index] = op1_val->data.x_array.fields[i]; } for (uint64_t i = 0; i < op2_type->data.array.len; i += 1, next_index += 1) { const_val->data.x_array.fields[next_index] = op2_val->data.x_array.fields[i]; } return get_array_type(g, child_type, new_len); } else if (op1_type->id == TypeTableEntryIdPointer) { if (!op1_val->data.x_ptr.is_c_str) { add_node_error(g, *op1, buf_sprintf("expected array or C string literal, got '%s'", buf_ptr(&op1_type->name))); return g->builtin_types.entry_invalid; } else if (!op2_val->data.x_ptr.is_c_str) { add_node_error(g, *op2, buf_sprintf("expected array or C string literal, got '%s'", buf_ptr(&op2_type->name))); return g->builtin_types.entry_invalid; } const_val->data.x_ptr.is_c_str = true; const_val->data.x_ptr.len = op1_val->data.x_ptr.len + op2_val->data.x_ptr.len - 1; const_val->data.x_ptr.ptr = allocate(const_val->data.x_ptr.len); uint64_t next_index = 0; for (uint64_t i = 0; i < op1_val->data.x_ptr.len - 1; i += 1, next_index += 1) { const_val->data.x_ptr.ptr[next_index] = op1_val->data.x_ptr.ptr[i]; } for (uint64_t i = 0; i < op2_val->data.x_ptr.len; i += 1, next_index += 1) { const_val->data.x_ptr.ptr[next_index] = op2_val->data.x_ptr.ptr[i]; } return op1_type; } else { zig_unreachable(); } } case BinOpTypeArrayMult: return analyze_array_mult(g, import, context, expected_type, node); case BinOpTypeInvalid: zig_unreachable(); } zig_unreachable(); } // Set name to nullptr to make the variable anonymous (not visible to programmer). static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import, BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node) { VariableTableEntry *variable_entry = allocate(1); variable_entry->type = type_entry; variable_entry->block_context = context; if (name) { buf_init_from_buf(&variable_entry->name, name); if (type_entry->id != TypeTableEntryIdInvalid) { VariableTableEntry *existing_var = find_variable(g, context, name); if (existing_var) { 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->type = g->builtin_types.entry_invalid; } else { auto primitive_table_entry = g->primitive_type_table.maybe_get(name); if (primitive_table_entry) { TypeTableEntry *type = primitive_table_entry->value; add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->type = g->builtin_types.entry_invalid; } else { AstNode *decl_node = find_decl(context, name); if (decl_node && decl_node->type != NodeTypeVariableDeclaration) { ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, decl_node, buf_sprintf("previous definition is here")); variable_entry->type = g->builtin_types.entry_invalid; } } } } context->var_table.put(&variable_entry->name, variable_entry); } else { // TODO replace _anon with @anon and make sure all tests still pass buf_init_from_str(&variable_entry->name, "_anon"); } if (context->fn_entry) { context->fn_entry->variable_list.append(variable_entry); } variable_entry->is_const = is_const; variable_entry->decl_node = source_node; variable_entry->val_node = val_node; return variable_entry; } static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) { AstNode *op1 = node->data.unwrap_err_expr.op1; AstNode *op2 = node->data.unwrap_err_expr.op2; AstNode *var_node = node->data.unwrap_err_expr.symbol; TypeTableEntry *lhs_type = analyze_expression(g, import, parent_context, nullptr, op1); if (lhs_type->id == TypeTableEntryIdInvalid) { return lhs_type; } else if (lhs_type->id == TypeTableEntryIdErrorUnion) { TypeTableEntry *child_type = lhs_type->data.error.child_type; BlockContext *child_context; if (var_node) { child_context = new_block_context(node, parent_context); var_node->block_context = child_context; Buf *var_name = &var_node->data.symbol_expr.symbol; node->data.unwrap_err_expr.var = add_local_var(g, var_node, import, child_context, var_name, g->builtin_types.entry_pure_error, true, nullptr); } else { child_context = parent_context; } analyze_expression(g, import, child_context, child_type, op2); return child_type; } else { add_node_error(g, op1, buf_sprintf("expected error type, got '%s'", buf_ptr(&lhs_type->name))); return g->builtin_types.entry_invalid; } } static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr) { bool is_const = variable_declaration->is_const; bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport); bool is_extern = variable_declaration->is_extern; TypeTableEntry *explicit_type = nullptr; if (variable_declaration->type != nullptr) { explicit_type = analyze_type_expr(g, import, context, variable_declaration->type); if (explicit_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, variable_declaration->type, buf_sprintf("variable of type 'unreachable' not allowed")); explicit_type = g->builtin_types.entry_invalid; } } TypeTableEntry *implicit_type = nullptr; if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) { implicit_type = explicit_type; } else if (variable_declaration->expr) { implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); if (implicit_type->id == TypeTableEntryIdInvalid) { // ignore the poison value } else if (expr_is_maybe) { if (implicit_type->id == TypeTableEntryIdMaybe) { if (var_is_ptr) { // TODO if the expression is constant, can't get pointer to it implicit_type = get_pointer_to_type(g, implicit_type->data.maybe.child_type, false); } else { implicit_type = implicit_type->data.maybe.child_type; } } else { add_node_error(g, variable_declaration->expr, buf_sprintf("expected maybe type")); implicit_type = g->builtin_types.entry_invalid; } } else if (implicit_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); implicit_type = g->builtin_types.entry_invalid; } else if ((!is_const || is_export) && (implicit_type->id == TypeTableEntryIdNumLitFloat || implicit_type->id == TypeTableEntryIdNumLitInt)) { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) { add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant")); implicit_type = g->builtin_types.entry_invalid; } if (implicit_type->id != TypeTableEntryIdInvalid && !context->fn_entry) { ConstExprValue *const_val = &get_resolved_expr(variable_declaration->expr)->const_val; if (!const_val->ok) { add_node_error(g, first_executing_node(variable_declaration->expr), buf_sprintf("global variable initializer requires constant expression")); } } } else if (!is_extern) { add_node_error(g, source_node, buf_sprintf("variables must be initialized")); implicit_type = g->builtin_types.entry_invalid; } TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser VariableTableEntry *var = add_local_var(g, source_node, import, context, &variable_declaration->symbol, type, is_const, expr_is_maybe ? nullptr : variable_declaration->expr); variable_declaration->variable = var; return var; } static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false, nullptr, false); } static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeNullLiteral); if (!expected_type) { add_node_error(g, node, buf_sprintf("unable to determine null type")); return g->builtin_types.entry_invalid; } if (expected_type->id != TypeTableEntryIdMaybe) { add_node_error(g, node, buf_sprintf("expected maybe type, got '%s'", buf_ptr(&expected_type->name))); return g->builtin_types.entry_invalid; } node->data.null_literal.resolved_struct_val_expr.type_entry = expected_type; node->data.null_literal.resolved_struct_val_expr.source_node = node; return resolve_expr_const_val_as_null(g, node, expected_type); } static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { Expr *expr = get_resolved_expr(node); ConstExprValue *const_val = &expr->const_val; const_val->ok = true; const_val->undef = true; return expected_type ? expected_type : g->builtin_types.entry_undef; } static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) { if (node->data.number_literal.overflow) { add_node_error(g, node, buf_sprintf("number literal too large to be represented in any type")); return g->builtin_types.entry_invalid; } if (node->data.number_literal.kind == NumLitUInt) { return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, node->data.number_literal.data.x_uint, false); } else if (node->data.number_literal.kind == NumLitFloat) { return resolve_expr_const_val_as_float_num_lit(g, node, expected_type, node->data.number_literal.data.x_float); } else { zig_unreachable(); } } static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNode *size_node = node->data.array_type.size; TypeTableEntry *child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type); if (child_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, node, buf_create_from_str("array of unreachable not allowed")); return g->builtin_types.entry_invalid; } else if (child_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } if (size_node) { TypeTableEntry *size_type = analyze_expression(g, import, context, g->builtin_types.entry_usize, size_node); if (size_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val; if (const_val->ok) { if (const_val->data.x_bignum.is_negative) { add_node_error(g, size_node, buf_sprintf("array size %s is negative", buf_ptr(bignum_to_buf(&const_val->data.x_bignum)))); return g->builtin_types.entry_invalid; } else { return resolve_expr_const_val_as_type(g, node, get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint), false); } } else if (context->fn_entry) { return resolve_expr_const_val_as_type(g, node, get_slice_type(g, child_type, node->data.array_type.is_const), false); } else { add_node_error(g, first_executing_node(size_node), buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } } else { return resolve_expr_const_val_as_type(g, node, get_slice_type(g, child_type, node->data.array_type.is_const), false); } } static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { TypeTableEntry *type_entry = analyze_fn_proto_type(g, import, context, expected_type, node, false, false); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } return resolve_expr_const_val_as_type(g, node, type_entry, false); } static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeWhileExpr); AstNode **condition_node = &node->data.while_expr.condition; AstNode *while_body_node = node->data.while_expr.body; AstNode **continue_expr_node = &node->data.while_expr.continue_expr; TypeTableEntry *condition_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *condition_node); if (*continue_expr_node) { analyze_expression(g, import, context, g->builtin_types.entry_void, *continue_expr_node); } BlockContext *child_context = new_block_context(node, context); child_context->parent_loop_node = node; analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node); TypeTableEntry *expr_return_type = g->builtin_types.entry_void; if (condition_type->id == TypeTableEntryIdInvalid) { expr_return_type = g->builtin_types.entry_invalid; } else { // if the condition is a simple constant expression and there are no break statements // then the return type is unreachable ConstExprValue *const_val = &get_resolved_expr(*condition_node)->const_val; if (const_val->ok) { if (const_val->data.x_bool) { node->data.while_expr.condition_always_true = true; if (!node->data.while_expr.contains_break) { expr_return_type = g->builtin_types.entry_unreachable; } } } } return expr_return_type; } static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeForExpr); AstNode *array_node = node->data.for_expr.array_expr; TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, array_node); TypeTableEntry *child_type; if (array_type->id == TypeTableEntryIdInvalid) { child_type = array_type; } else if (array_type->id == TypeTableEntryIdArray) { child_type = array_type->data.array.child_type; } else if (array_type->id == TypeTableEntryIdStruct && array_type->data.structure.is_slice) { TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry; assert(pointer_type->id == TypeTableEntryIdPointer); child_type = pointer_type->data.pointer.child_type; } else { add_node_error(g, node, buf_sprintf("iteration over non array type '%s'", buf_ptr(&array_type->name))); child_type = g->builtin_types.entry_invalid; } TypeTableEntry *var_type; if (child_type->id != TypeTableEntryIdInvalid && node->data.for_expr.elem_is_ptr) { var_type = get_pointer_to_type(g, child_type, false); } else { var_type = child_type; } BlockContext *child_context = new_block_context(node, context); child_context->parent_loop_node = node; AstNode *elem_var_node = node->data.for_expr.elem_node; elem_var_node->block_context = child_context; Buf *elem_var_name = &elem_var_node->data.symbol_expr.symbol; node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, child_context, elem_var_name, var_type, true, nullptr); AstNode *index_var_node = node->data.for_expr.index_node; if (index_var_node) { Buf *index_var_name = &index_var_node->data.symbol_expr.symbol; index_var_node->block_context = child_context; node->data.for_expr.index_var = add_local_var(g, index_var_node, import, child_context, index_var_name, g->builtin_types.entry_usize, true, nullptr); } else { node->data.for_expr.index_var = add_local_var(g, node, import, child_context, nullptr, g->builtin_types.entry_usize, true, nullptr); } AstNode *for_body_node = node->data.for_expr.body; analyze_expression(g, import, child_context, g->builtin_types.entry_void, for_body_node); return g->builtin_types.entry_void; } static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeBreak); AstNode *loop_node = context->parent_loop_node; if (loop_node) { if (loop_node->type == NodeTypeWhileExpr) { loop_node->data.while_expr.contains_break = true; } else if (loop_node->type == NodeTypeForExpr) { loop_node->data.for_expr.contains_break = true; } else { zig_unreachable(); } } else { add_node_error(g, node, buf_sprintf("'break' expression outside loop")); } return g->builtin_types.entry_unreachable; } static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNode *loop_node = context->parent_loop_node; if (loop_node) { if (loop_node->type == NodeTypeWhileExpr) { loop_node->data.while_expr.contains_continue = true; } else if (loop_node->type == NodeTypeForExpr) { loop_node->data.for_expr.contains_continue = true; } else { zig_unreachable(); } } else { add_node_error(g, node, buf_sprintf("'continue' expression outside loop")); } return g->builtin_types.entry_unreachable; } static TypeTableEntry *add_error_if_type_is_num_lit(CodeGen *g, TypeTableEntry *type_entry, AstNode *source_node) { if (type_entry->id == TypeTableEntryIdNumLitInt || type_entry->id == TypeTableEntryIdNumLitFloat) { add_node_error(g, source_node, buf_sprintf("unable to infer expression type")); return g->builtin_types.entry_invalid; } else { return type_entry; } } static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node, AstNode **then_node, AstNode **else_node, bool cond_is_const, bool cond_bool_val) { if (!*else_node) { *else_node = create_ast_void_node(g, import, node); normalize_parent_ptrs(node); } BlockContext *then_context; BlockContext *else_context; if (cond_is_const) { if (cond_bool_val) { then_context = parent_context; else_context = new_block_context(node, parent_context); else_context->codegen_excluded = true; } else { then_context = new_block_context(node, parent_context); else_context = parent_context; then_context->codegen_excluded = true; } } else { then_context = parent_context; else_context = parent_context; } TypeTableEntry *then_type = nullptr; TypeTableEntry *else_type = nullptr; if (!then_context->codegen_excluded) { then_type = analyze_expression(g, import, then_context, expected_type, *then_node); if (then_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } } if (!else_context->codegen_excluded) { else_type = analyze_expression(g, import, else_context, expected_type, *else_node); if (else_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } } TypeTableEntry *result_type; if (then_context->codegen_excluded) { result_type = else_type; } else if (else_context->codegen_excluded) { result_type = then_type; } else if (expected_type) { result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type; } else { AstNode *op_nodes[] = {*then_node, *else_node}; TypeTableEntry *op_types[] = {then_type, else_type}; result_type = resolve_peer_type_compatibility(g, import, parent_context, node, op_nodes, op_types, 2); } if (!cond_is_const) { return add_error_if_type_is_num_lit(g, result_type, node); } ConstExprValue *other_const_val; if (cond_bool_val) { other_const_val = &get_resolved_expr(*then_node)->const_val; } else { other_const_val = &get_resolved_expr(*else_node)->const_val; } if (!other_const_val->ok) { return add_error_if_type_is_num_lit(g, result_type, node); } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; *const_val = *other_const_val; // the condition depends on a compile var, so the entire if statement does too const_val->depends_on_compile_var = true; return result_type; } static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNode **cond = &node->data.if_bool_expr.condition; TypeTableEntry *cond_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *cond); if (cond_type->id == TypeTableEntryIdInvalid) { return cond_type; } ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val; if (cond_val->undef) { add_node_error(g, first_executing_node(*cond), buf_sprintf("branch on undefined value")); return cond_type; } if (cond_val->ok && !cond_val->depends_on_compile_var) { const char *str_val = cond_val->data.x_bool ? "true" : "false"; add_node_error(g, first_executing_node(*cond), buf_sprintf("condition is always %s; unnecessary if statement", str_val)); } bool cond_is_const = cond_val->ok; bool cond_bool_val = cond_val->data.x_bool; AstNode **then_node = &node->data.if_bool_expr.then_block; AstNode **else_node = &node->data.if_bool_expr.else_node; return analyze_if(g, import, context, expected_type, node, then_node, else_node, cond_is_const, cond_bool_val); } static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeIfVarExpr); BlockContext *child_context = new_block_context(node, parent_context); analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true, nullptr, node->data.if_var_expr.var_is_ptr); VariableTableEntry *var = node->data.if_var_expr.var_decl.variable; if (var->type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr; ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val; bool cond_is_const = var_const_val->ok; bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false; AstNode **then_node = &node->data.if_var_expr.then_block; AstNode **else_node = &node->data.if_var_expr.else_node; return analyze_if(g, import, child_context, expected_type, node, then_node, else_node, cond_is_const, cond_bool_val); } static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, const char *err_format, bool is_max) { assert(node->type == NodeTypeFnCallExpr); assert(node->data.fn_call_expr.params.length == 1); AstNode *type_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); if (type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdFloat || type_entry->id == TypeTableEntryIdBool) { eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max); return type_entry; } else { add_node_error(g, node, buf_sprintf(err_format, buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } } static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node, AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca) { node->data.fn_call_expr.cast_op = op; ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry; ConstExprValue *const_val = &get_resolved_expr(node)->const_val; if (other_val->ok) { eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type, const_val, wanted_type); } if (need_alloca) { if (context->fn_entry) { context->fn_entry->cast_alloca_list.append(node); } else { assert(get_resolved_expr(node)->const_val.ok); } } return wanted_type; } static bool type_is_codegen_pointer(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return true; if (type->id == TypeTableEntryIdFn) return true; if (type->id == TypeTableEntryIdMaybe) { if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return true; if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return true; } return false; } static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; int actual_param_count = node->data.fn_call_expr.params.length; if (actual_param_count != 1) { add_node_error(g, fn_ref_expr, buf_sprintf("cast expression expects exactly one parameter")); return g->builtin_types.entry_invalid; } AstNode *expr_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *wanted_type = resolve_type(g, fn_ref_expr); TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, expr_node); TypeTableEntry *wanted_type_canon = get_underlying_type(wanted_type); TypeTableEntry *actual_type_canon = get_underlying_type(actual_type); if (wanted_type_canon->id == TypeTableEntryIdInvalid || actual_type_canon->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } // explicit match or non-const to const if (types_match_const_cast_only(wanted_type, actual_type)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNoop, false); } // explicit cast from bool to int if (wanted_type_canon->id == TypeTableEntryIdInt && actual_type_canon->id == TypeTableEntryIdBool) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBoolToInt, false); } // explicit cast from pointer to isize or usize if ((wanted_type_canon == g->builtin_types.entry_isize || wanted_type_canon == g->builtin_types.entry_usize) && type_is_codegen_pointer(actual_type_canon)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPtrToInt, false); } // explicit cast from isize or usize to pointer if (wanted_type_canon->id == TypeTableEntryIdPointer && (actual_type_canon == g->builtin_types.entry_isize || actual_type_canon == g->builtin_types.entry_usize)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToPtr, false); } // explicit widening or shortening cast if ((wanted_type_canon->id == TypeTableEntryIdInt && actual_type_canon->id == TypeTableEntryIdInt) || (wanted_type_canon->id == TypeTableEntryIdFloat && actual_type_canon->id == TypeTableEntryIdFloat)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpWidenOrShorten, false); } // explicit cast from int to float if (wanted_type_canon->id == TypeTableEntryIdFloat && actual_type_canon->id == TypeTableEntryIdInt) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToFloat, false); } // explicit cast from float to int if (wanted_type_canon->id == TypeTableEntryIdInt && actual_type_canon->id == TypeTableEntryIdFloat) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpFloatToInt, false); } // explicit cast from array to slice if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray && types_match_const_cast_only( wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type, actual_type->data.array.child_type)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpToUnknownSizeArray, true); } // explicit cast from []T to []u8 or []u8 to []T if (is_slice(wanted_type) && is_slice(actual_type) && (is_u8(wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type) || is_u8(actual_type->data.structure.fields[0].type_entry->data.pointer.child_type)) && (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const || !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const)) { mark_impure_fn(context); return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true); } // explicit cast from [N]u8 to []T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray && is_u8(actual_type->data.array.child_type)) { mark_impure_fn(context); uint64_t child_type_size = type_size(g, wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type); if (actual_type->data.array.len % child_type_size == 0) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBytesToSlice, true); } else { add_node_error(g, node, buf_sprintf("unable to convert %s to %s: size mismatch", buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); return g->builtin_types.entry_invalid; } } // explicit cast from pointer to another pointer if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) && (wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPointerReinterpret, false); } // explicit cast from maybe pointer to another maybe pointer if (actual_type->id == TypeTableEntryIdMaybe && (actual_type->data.maybe.child_type->id == TypeTableEntryIdPointer || actual_type->data.maybe.child_type->id == TypeTableEntryIdFn) && wanted_type->id == TypeTableEntryIdMaybe && (wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer || wanted_type->data.maybe.child_type->id == TypeTableEntryIdFn)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPointerReinterpret, false); } // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) { get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull; return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) { if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) { get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull; return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); } else { return g->builtin_types.entry_invalid; } } } // explicit cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) { get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError; return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) { if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) { get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError; return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); } else { return g->builtin_types.entry_invalid; } } } // explicit cast from pure error to error union type if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdPureError) { get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownError; return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false); } // explicit cast from number literal to another type if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) { if (num_lit_fits_in_other_type(g, expr_node, wanted_type_canon)) { CastOp op; if ((actual_type->id == TypeTableEntryIdNumLitFloat && wanted_type_canon->id == TypeTableEntryIdFloat) || (actual_type->id == TypeTableEntryIdNumLitInt && wanted_type_canon->id == TypeTableEntryIdInt)) { op = CastOpNoop; } else if (wanted_type_canon->id == TypeTableEntryIdInt) { op = CastOpFloatToInt; } else if (wanted_type_canon->id == TypeTableEntryIdFloat) { op = CastOpIntToFloat; } else { zig_unreachable(); } return resolve_cast(g, context, node, expr_node, wanted_type, op, false); } else { return g->builtin_types.entry_invalid; } } // explicit cast from %void to integer type which can fit it bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && !type_has_bits(actual_type->data.error.child_type); bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError; if ((actual_type_is_void_err || actual_type_is_pure_err) && wanted_type->id == TypeTableEntryIdInt) { BigNum bn; bignum_init_unsigned(&bn, g->error_decls.length); if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrToInt, false); } else { add_node_error(g, node, buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); return g->builtin_types.entry_invalid; } } // explicit cast from integer to enum type with no payload if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum && wanted_type->data.enumeration.gen_field_count == 0) { return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToEnum, false); } add_node_error(g, node, buf_sprintf("invalid cast from type '%s' to '%s'", buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); return g->builtin_types.entry_invalid; } static TypeTableEntry *resolve_expr_const_val_as_import(CodeGen *g, AstNode *node, ImportTableEntry *import) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_import = import; return g->builtin_types.entry_namespace; } static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); if (context->fn_entry) { add_node_error(g, node, buf_sprintf("@import invalid inside function bodies")); return g->builtin_types.entry_invalid; } AstNode *first_param_node = node->data.fn_call_expr.params.at(0); Buf *import_target_str = resolve_const_expr_str(g, import, context, first_param_node->parent_field); if (!import_target_str) { return g->builtin_types.entry_invalid; } Buf *import_target_path; Buf *search_dir; assert(import->package); PackageTableEntry *target_package; auto package_entry = import->package->package_table.maybe_get(import_target_str); if (package_entry) { target_package = package_entry->value; import_target_path = &target_package->root_src_path; search_dir = &target_package->root_src_dir; } else { // try it as a filename target_package = import->package; import_target_path = import_target_str; search_dir = &import->package->root_src_dir; } Buf full_path = BUF_INIT; os_path_join(search_dir, import_target_path, &full_path); Buf *import_code = buf_alloc(); Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(&full_path, abs_full_path))) { if (err == ErrorFileNotFound) { add_node_error(g, node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); return g->builtin_types.entry_invalid; } else { g->error_during_imports = true; add_node_error(g, node, buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); return g->builtin_types.entry_invalid; } } auto import_entry = g->import_table.maybe_get(abs_full_path); if (import_entry) { return resolve_expr_const_val_as_import(g, node, import_entry->value); } if ((err = os_fetch_file_path(abs_full_path, import_code))) { if (err == ErrorFileNotFound) { add_node_error(g, node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); return g->builtin_types.entry_invalid; } else { add_node_error(g, node, buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); return g->builtin_types.entry_invalid; } } ImportTableEntry *target_import = add_source_file(g, target_package, abs_full_path, search_dir, import_target_path, import_code); scan_decls(g, target_import, target_import->block_context, target_import->root); return resolve_expr_const_val_as_import(g, node, target_import); } static TypeTableEntry *analyze_c_import(CodeGen *g, ImportTableEntry *parent_import, BlockContext *parent_context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); if (parent_context->fn_entry) { add_node_error(g, node, buf_sprintf("@c_import invalid inside function bodies")); return g->builtin_types.entry_invalid; } AstNode *block_node = node->data.fn_call_expr.params.at(0); BlockContext *child_context = new_block_context(node, parent_context); child_context->c_import_buf = buf_alloc(); TypeTableEntry *resolved_type = analyze_expression(g, parent_import, child_context, g->builtin_types.entry_void, block_node); if (resolved_type->id == TypeTableEntryIdInvalid) { return resolved_type; } find_libc_include_path(g); ImportTableEntry *child_import = allocate(1); child_import->c_import_node = node; ZigList errors = {0}; int err; if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g, node))) { zig_panic("unable to parse h file: %s\n", err_str(err)); } if (errors.length > 0) { ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed")); for (int i = 0; i < errors.length; i += 1) { ErrorMsg *err_msg = errors.at(i); err_msg_add_note(parent_err_msg, err_msg); } return g->builtin_types.entry_invalid; } if (g->verbose) { fprintf(stderr, "\nc_import:\n"); fprintf(stderr, "-----------\n"); ast_render(stderr, child_import->root, 4); } child_import->di_file = parent_import->di_file; child_import->block_context = new_block_context(child_import->root, nullptr); scan_decls(g, child_import, child_import->block_context, child_import->root); return resolve_expr_const_val_as_import(g, node, child_import); } static TypeTableEntry *analyze_err_name(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode *err_value = node->data.fn_call_expr.params.at(0); TypeTableEntry *resolved_type = analyze_expression(g, import, context, g->builtin_types.entry_pure_error, err_value); if (resolved_type->id == TypeTableEntryIdInvalid) { return resolved_type; } g->generate_error_name_table = true; TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); return str_type; } static TypeTableEntry *analyze_embed_file(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode **first_param_node = &node->data.fn_call_expr.params.at(0); Buf *rel_file_path = resolve_const_expr_str(g, import, context, first_param_node); if (!rel_file_path) { return g->builtin_types.entry_invalid; } // figure out absolute path to resource Buf source_dir_path = BUF_INIT; os_path_dirname(import->path, &source_dir_path); Buf file_path = BUF_INIT; os_path_resolve(&source_dir_path, rel_file_path, &file_path); // load from file system into const expr Buf file_contents = BUF_INIT; int err; if ((err = os_fetch_file_path(&file_path, &file_contents))) { if (err == ErrorFileNotFound) { add_node_error(g, node, buf_sprintf("unable to find '%s'", buf_ptr(&file_path))); return g->builtin_types.entry_invalid; } else { add_node_error(g, node, buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err))); return g->builtin_types.entry_invalid; } } // TODO add dependency on the file we embedded so that we know if it changes // we'll have to invalidate the cache return resolve_expr_const_val_as_string_lit(g, node, &file_contents); } static TypeTableEntry *analyze_cmpxchg(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode **ptr_arg = &node->data.fn_call_expr.params.at(0); AstNode **cmp_arg = &node->data.fn_call_expr.params.at(1); AstNode **new_arg = &node->data.fn_call_expr.params.at(2); AstNode **success_order_arg = &node->data.fn_call_expr.params.at(3); AstNode **failure_order_arg = &node->data.fn_call_expr.params.at(4); TypeTableEntry *ptr_type = analyze_expression(g, import, context, nullptr, *ptr_arg); if (ptr_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (ptr_type->id != TypeTableEntryIdPointer) { add_node_error(g, *ptr_arg, buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&ptr_type->name))); return g->builtin_types.entry_invalid; } TypeTableEntry *child_type = ptr_type->data.pointer.child_type; TypeTableEntry *cmp_type = analyze_expression(g, import, context, child_type, *cmp_arg); TypeTableEntry *new_type = analyze_expression(g, import, context, child_type, *new_arg); TypeTableEntry *success_order_type = analyze_expression(g, import, context, g->builtin_types.entry_atomic_order_enum, *success_order_arg); TypeTableEntry *failure_order_type = analyze_expression(g, import, context, g->builtin_types.entry_atomic_order_enum, *failure_order_arg); if (cmp_type->id == TypeTableEntryIdInvalid || new_type->id == TypeTableEntryIdInvalid || success_order_type->id == TypeTableEntryIdInvalid || failure_order_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } ConstExprValue *success_order_val = &get_resolved_expr(*success_order_arg)->const_val; ConstExprValue *failure_order_val = &get_resolved_expr(*failure_order_arg)->const_val; if (!success_order_val->ok) { add_node_error(g, *success_order_arg, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } else if (!failure_order_val->ok) { add_node_error(g, *failure_order_arg, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } if (success_order_val->data.x_enum.tag < AtomicOrderMonotonic) { add_node_error(g, *success_order_arg, buf_sprintf("success atomic ordering must be Monotonic or stricter")); return g->builtin_types.entry_invalid; } if (failure_order_val->data.x_enum.tag < AtomicOrderMonotonic) { add_node_error(g, *failure_order_arg, buf_sprintf("failure atomic ordering must be Monotonic or stricter")); return g->builtin_types.entry_invalid; } if (failure_order_val->data.x_enum.tag > success_order_val->data.x_enum.tag) { add_node_error(g, *failure_order_arg, buf_sprintf("failure atomic ordering must be no stricter than success")); return g->builtin_types.entry_invalid; } if (failure_order_val->data.x_enum.tag == AtomicOrderRelease || failure_order_val->data.x_enum.tag == AtomicOrderAcqRel) { add_node_error(g, *failure_order_arg, buf_sprintf("failure atomic ordering must not be Release or AcqRel")); return g->builtin_types.entry_invalid; } return g->builtin_types.entry_bool; } static TypeTableEntry *analyze_fence(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode **atomic_order_arg = &node->data.fn_call_expr.params.at(0); TypeTableEntry *atomic_order_type = analyze_expression(g, import, context, g->builtin_types.entry_atomic_order_enum, *atomic_order_arg); if (atomic_order_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } ConstExprValue *atomic_order_val = &get_resolved_expr(*atomic_order_arg)->const_val; if (!atomic_order_val->ok) { add_node_error(g, *atomic_order_arg, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } return g->builtin_types.entry_void; } static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode **op1 = &node->data.fn_call_expr.params.at(0); AstNode **op2 = &node->data.fn_call_expr.params.at(1); TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1); TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2); AstNode *op_nodes[] = {*op1, *op2}; TypeTableEntry *op_types[] = {op1_type, op2_type}; TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2); if (result_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (result_type->id == TypeTableEntryIdInt) { return result_type; } else if (result_type->id == TypeTableEntryIdNumLitInt) { // check for division by zero // check for non exact division zig_panic("TODO"); } else { add_node_error(g, node, buf_sprintf("expected integer type, got '%s'", buf_ptr(&result_type->name))); return g->builtin_types.entry_invalid; } } static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode **op1 = &node->data.fn_call_expr.params.at(0); AstNode **op2 = &node->data.fn_call_expr.params.at(1); TypeTableEntry *dest_type = analyze_type_expr(g, import, context, *op1); TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, *op2); if (dest_type->id == TypeTableEntryIdInvalid || src_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (dest_type->id != TypeTableEntryIdInt) { add_node_error(g, *op1, buf_sprintf("expected integer type, got '%s'", buf_ptr(&dest_type->name))); return g->builtin_types.entry_invalid; } else if (src_type->id != TypeTableEntryIdInt) { add_node_error(g, *op2, buf_sprintf("expected integer type, got '%s'", buf_ptr(&src_type->name))); return g->builtin_types.entry_invalid; } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) { const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned"; add_node_error(g, *op2, buf_sprintf("expected %s integer type, got '%s'", sign_str, buf_ptr(&src_type->name))); return g->builtin_types.entry_invalid; } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) { add_node_error(g, *op2, buf_sprintf("type '%s' has same or fewer bits than destination type '%s'", buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); return g->builtin_types.entry_invalid; } // TODO const expr eval return dest_type; } static TypeTableEntry *analyze_compile_err(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { AstNode *first_param_node = node->data.fn_call_expr.params.at(0); Buf *err_msg = resolve_const_expr_str(g, import, context, first_param_node->parent_field); if (!err_msg) { return g->builtin_types.entry_invalid; } add_node_error(g, node, err_msg); return g->builtin_types.entry_invalid; } static TypeTableEntry *analyze_int_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { AstNode **is_signed_node = &node->data.fn_call_expr.params.at(0); AstNode **bit_count_node = &node->data.fn_call_expr.params.at(1); AstNode **is_wrap_node = &node->data.fn_call_expr.params.at(2); TypeTableEntry *bool_type = g->builtin_types.entry_bool; TypeTableEntry *usize_type = g->builtin_types.entry_usize; TypeTableEntry *is_signed_type = analyze_expression(g, import, context, bool_type, *is_signed_node); TypeTableEntry *bit_count_type = analyze_expression(g, import, context, usize_type, *bit_count_node); TypeTableEntry *is_wrap_type = analyze_expression(g, import, context, bool_type, *is_wrap_node); if (is_signed_type->id == TypeTableEntryIdInvalid || bit_count_type->id == TypeTableEntryIdInvalid || is_wrap_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } ConstExprValue *is_signed_val = &get_resolved_expr(*is_signed_node)->const_val; ConstExprValue *bit_count_val = &get_resolved_expr(*bit_count_node)->const_val; ConstExprValue *is_wrap_val = &get_resolved_expr(*is_wrap_node)->const_val; AstNode *bad_node = nullptr; if (!is_signed_val->ok) { bad_node = *is_signed_node; } else if (!bit_count_val->ok) { bad_node = *bit_count_node; } else if (!is_wrap_val->ok) { bad_node = *is_wrap_node; } if (bad_node) { add_node_error(g, bad_node, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } bool depends_on_compile_var = is_signed_val->depends_on_compile_var || bit_count_val->depends_on_compile_var || is_wrap_val->depends_on_compile_var; TypeTableEntry *int_type = get_int_type(g, is_signed_val->data.x_bool, is_wrap_val->data.x_bool, bit_count_val->data.x_bignum.data.x_uint); return resolve_expr_const_val_as_type(g, node, int_type, depends_on_compile_var); } static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; Buf *name = &fn_ref_expr->data.symbol_expr.symbol; auto entry = g->builtin_fn_table.maybe_get(name); if (!entry) { add_node_error(g, node, buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); return g->builtin_types.entry_invalid; } BuiltinFnEntry *builtin_fn = entry->value; int actual_param_count = node->data.fn_call_expr.params.length; node->data.fn_call_expr.builtin_fn = builtin_fn; if (builtin_fn->param_count != actual_param_count) { add_node_error(g, node, buf_sprintf("expected %d arguments, got %d", builtin_fn->param_count, actual_param_count)); return g->builtin_types.entry_invalid; } builtin_fn->ref_count += 1; switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); case BuiltinFnIdAddWithOverflow: case BuiltinFnIdSubWithOverflow: case BuiltinFnIdMulWithOverflow: case BuiltinFnIdShlWithOverflow: { AstNode *type_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node); if (int_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_bool; } else if (int_type->id == TypeTableEntryIdInt) { AstNode *op1_node = node->data.fn_call_expr.params.at(1); AstNode *op2_node = node->data.fn_call_expr.params.at(2); AstNode *result_node = node->data.fn_call_expr.params.at(3); analyze_expression(g, import, context, int_type, op1_node); analyze_expression(g, import, context, int_type, op2_node); analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false), result_node); } else { add_node_error(g, type_node, buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name))); } // TODO constant expression evaluation return g->builtin_types.entry_bool; } case BuiltinFnIdMemcpy: { AstNode *dest_node = node->data.fn_call_expr.params.at(0); AstNode *src_node = node->data.fn_call_expr.params.at(1); AstNode *len_node = node->data.fn_call_expr.params.at(2); TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node); TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, src_node); analyze_expression(g, import, context, builtin_fn->param_types[2], len_node); if (dest_type->id != TypeTableEntryIdInvalid && dest_type->id != TypeTableEntryIdPointer) { add_node_error(g, dest_node, buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name))); } if (src_type->id != TypeTableEntryIdInvalid && src_type->id != TypeTableEntryIdPointer) { add_node_error(g, src_node, buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&src_type->name))); } if (dest_type->id == TypeTableEntryIdPointer && src_type->id == TypeTableEntryIdPointer) { uint64_t dest_align = get_memcpy_align(g, dest_type->data.pointer.child_type); uint64_t src_align = get_memcpy_align(g, src_type->data.pointer.child_type); if (dest_align != src_align) { add_node_error(g, dest_node, buf_sprintf( "misaligned memcpy, '%s' has alignment '%" PRIu64 ", '%s' has alignment %" PRIu64, buf_ptr(&dest_type->name), dest_align, buf_ptr(&src_type->name), src_align)); } } return builtin_fn->return_type; } case BuiltinFnIdMemset: { AstNode *dest_node = node->data.fn_call_expr.params.at(0); AstNode *char_node = node->data.fn_call_expr.params.at(1); AstNode *len_node = node->data.fn_call_expr.params.at(2); TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node); analyze_expression(g, import, context, builtin_fn->param_types[1], char_node); analyze_expression(g, import, context, builtin_fn->param_types[2], len_node); if (dest_type->id != TypeTableEntryIdInvalid && dest_type->id != TypeTableEntryIdPointer) { add_node_error(g, dest_node, buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name))); } return builtin_fn->return_type; } case BuiltinFnIdSizeof: { AstNode *type_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); if (type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (type_entry->id == TypeTableEntryIdUnreachable) { add_node_error(g, first_executing_node(type_node), buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } else { uint64_t size_in_bytes = type_size(g, type_entry); bool depends_on_compile_var = (type_entry == g->builtin_types.entry_usize || type_entry == g->builtin_types.entry_isize); return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, size_in_bytes, depends_on_compile_var); } } case BuiltinFnIdAlignof: { AstNode *type_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); if (type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (type_entry->id == TypeTableEntryIdUnreachable) { add_node_error(g, first_executing_node(type_node), buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } else { uint64_t align_in_bytes = LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref); return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, align_in_bytes, false); } } case BuiltinFnIdMaxValue: return analyze_min_max_value(g, import, context, node, "no max value available for type '%s'", true); case BuiltinFnIdMinValue: return analyze_min_max_value(g, import, context, node, "no min value available for type '%s'", false); case BuiltinFnIdMemberCount: { AstNode *type_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdEnum) { uint64_t value_count = type_entry->data.enumeration.field_count; return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, value_count, false); } else { add_node_error(g, node, buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } } case BuiltinFnIdTypeof: { AstNode *expr_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); switch (type_entry->id) { case TypeTableEntryIdInvalid: return type_entry; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: add_node_error(g, expr_node, buf_sprintf("type '%s' not eligible for @typeof", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: return resolve_expr_const_val_as_type(g, node, type_entry, false); } } case BuiltinFnIdCInclude: { if (!context->c_import_buf) { add_node_error(g, node, buf_sprintf("@c_include valid only in c_import blocks")); return g->builtin_types.entry_invalid; } AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field; TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *str_node); if (resolved_type->id == TypeTableEntryIdInvalid) { return resolved_type; } ConstExprValue *const_str_val = &get_resolved_expr(*str_node)->const_val; if (!const_str_val->ok) { add_node_error(g, *str_node, buf_sprintf("@c_include requires constant expression")); return g->builtin_types.entry_void; } buf_appendf(context->c_import_buf, "#include <"); ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; uint64_t len = ptr_field->data.x_ptr.len; for (uint64_t i = 0; i < len; i += 1) { ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i]; uint64_t big_c = char_val->data.x_bignum.data.x_uint; assert(big_c <= UINT8_MAX); uint8_t c = big_c; buf_append_char(context->c_import_buf, c); } buf_appendf(context->c_import_buf, ">\n"); return g->builtin_types.entry_void; } case BuiltinFnIdCDefine: zig_panic("TODO"); case BuiltinFnIdCUndef: zig_panic("TODO"); case BuiltinFnIdCompileVar: { AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field; Buf *var_name = resolve_const_expr_str(g, import, context, str_node); if (!var_name) { return g->builtin_types.entry_invalid; } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; const_val->depends_on_compile_var = true; if (buf_eql_str(var_name, "is_big_endian")) { return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true); } else if (buf_eql_str(var_name, "is_release")) { return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true); } else if (buf_eql_str(var_name, "is_test")) { return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true); } else if (buf_eql_str(var_name, "os")) { const_val->data.x_enum.tag = g->target_os_index; return g->builtin_types.entry_os_enum; } else if (buf_eql_str(var_name, "arch")) { const_val->data.x_enum.tag = g->target_arch_index; return g->builtin_types.entry_arch_enum; } else if (buf_eql_str(var_name, "environ")) { const_val->data.x_enum.tag = g->target_environ_index; return g->builtin_types.entry_environ_enum; } else { add_node_error(g, *str_node, buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name))); return g->builtin_types.entry_invalid; } } case BuiltinFnIdConstEval: { AstNode **expr_node = node->data.fn_call_expr.params.at(0)->parent_field; TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_type, *expr_node); if (resolved_type->id == TypeTableEntryIdInvalid) { return resolved_type; } ConstExprValue *const_expr_val = &get_resolved_expr(*expr_node)->const_val; if (!const_expr_val->ok) { add_node_error(g, *expr_node, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; *const_val = *const_expr_val; return resolved_type; } case BuiltinFnIdCtz: case BuiltinFnIdClz: { AstNode *type_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node); if (int_type->id == TypeTableEntryIdInvalid) { return int_type; } else if (int_type->id == TypeTableEntryIdInt) { AstNode **expr_node = node->data.fn_call_expr.params.at(1)->parent_field; TypeTableEntry *resolved_type = analyze_expression(g, import, context, int_type, *expr_node); if (resolved_type->id == TypeTableEntryIdInvalid) { return resolved_type; } // TODO const expr eval return resolved_type; } else { add_node_error(g, type_node, buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name))); return g->builtin_types.entry_invalid; } } case BuiltinFnIdImport: return analyze_import(g, import, context, node); case BuiltinFnIdCImport: return analyze_c_import(g, import, context, node); case BuiltinFnIdErrName: return analyze_err_name(g, import, context, node); case BuiltinFnIdBreakpoint: mark_impure_fn(context); return g->builtin_types.entry_void; case BuiltinFnIdReturnAddress: case BuiltinFnIdFrameAddress: mark_impure_fn(context); return builtin_fn->return_type; case BuiltinFnIdEmbedFile: return analyze_embed_file(g, import, context, node); case BuiltinFnIdCmpExchange: return analyze_cmpxchg(g, import, context, node); case BuiltinFnIdFence: return analyze_fence(g, import, context, node); case BuiltinFnIdDivExact: return analyze_div_exact(g, import, context, node); case BuiltinFnIdTruncate: return analyze_truncate(g, import, context, node); case BuiltinFnIdCompileErr: return analyze_compile_err(g, import, context, node); case BuiltinFnIdIntType: return analyze_int_type(g, import, context, node); } zig_unreachable(); } // Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known // at compile time. Otherwise this is a function pointer call. static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, AstNode *struct_node) { assert(node->type == NodeTypeFnCallExpr); if (fn_type->id == TypeTableEntryIdInvalid) { return fn_type; } // The function call might include inline parameters which we need to ignore according to the // fn_type. FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; AstNode *generic_proto_node = fn_table_entry ? fn_table_entry->proto_node->data.fn_proto.generic_proto_node : nullptr; // count parameters int struct_node_1_or_0 = struct_node ? 1 : 0; int src_param_count = fn_type->data.fn.fn_type_id.param_count + (generic_proto_node ? generic_proto_node->data.fn_proto.inline_arg_count : 0); int call_param_count = node->data.fn_call_expr.params.length; bool ok_invocation = true; if (fn_type->data.fn.fn_type_id.is_var_args) { if (call_param_count < src_param_count - struct_node_1_or_0) { ok_invocation = false; add_node_error(g, node, buf_sprintf("expected at least %d arguments, got %d", src_param_count, call_param_count)); } } else if (src_param_count - struct_node_1_or_0 != call_param_count) { ok_invocation = false; add_node_error(g, node, buf_sprintf("expected %d arguments, got %d", src_param_count, call_param_count)); } bool all_args_const_expr = true; if (struct_node) { ConstExprValue *struct_const_val = &get_resolved_expr(struct_node)->const_val; if (!struct_const_val->ok) { all_args_const_expr = false; } } // analyze each parameter. in the case of a method, we already analyzed the // first parameter in order to figure out which struct we were calling a method on. int next_type_i = struct_node_1_or_0; for (int call_i = 0; call_i < call_param_count; call_i += 1) { int proto_i = call_i + struct_node_1_or_0; AstNode **param_node = &node->data.fn_call_expr.params.at(call_i); // determine the expected type for each parameter TypeTableEntry *expected_param_type = nullptr; if (proto_i < src_param_count) { if (generic_proto_node && generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline) { continue; } FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[next_type_i]; next_type_i += 1; expected_param_type = param_info->type; } TypeTableEntry *param_type = analyze_expression(g, import, context, expected_param_type, *param_node); if (param_type->id == TypeTableEntryIdInvalid) { return param_type; } ConstExprValue *const_arg_val = &get_resolved_expr(*param_node)->const_val; if (!const_arg_val->ok) { all_args_const_expr = false; } } TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; if (return_type->id == TypeTableEntryIdInvalid) { return return_type; } ConstExprValue *result_val = &get_resolved_expr(node)->const_val; if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) { if (fn_table_entry->anal_state == FnAnalStateReady) { analyze_fn_body(g, fn_table_entry); } if (all_args_const_expr) { if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) { if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) { // function evaluation generated an error return g->builtin_types.entry_invalid; } return return_type; } } } if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure || fn_table_entry->want_pure == WantPureFalse) { // calling an impure fn is impure mark_impure_fn(context); } if (handle_is_ptr(return_type)) { if (context->fn_entry) { context->fn_entry->cast_alloca_list.append(node); } else if (!result_val->ok) { add_node_error(g, node, buf_sprintf("unable to evaluate constant expression")); } } return return_type; } static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *call_node, FnTableEntry *fn_table_entry, AstNode *struct_node) { assert(call_node->type == NodeTypeFnCallExpr); assert(fn_table_entry); AstNode *decl_node = fn_table_entry->proto_node; // count parameters int struct_node_1_or_0 = (struct_node ? 1 : 0); int src_param_count = decl_node->data.fn_proto.params.length; int call_param_count = call_node->data.fn_call_expr.params.length; if (src_param_count != call_param_count + struct_node_1_or_0) { add_node_error(g, call_node, buf_sprintf("expected %d arguments, got %d", src_param_count, call_param_count)); return g->builtin_types.entry_invalid; } int inline_arg_count = decl_node->data.fn_proto.inline_arg_count; assert(inline_arg_count > 0); BlockContext *child_context = decl_node->owner->block_context; int next_generic_param_index = 0; GenericFnTypeId *generic_fn_type_id = allocate(1); generic_fn_type_id->decl_node = decl_node; generic_fn_type_id->generic_param_count = inline_arg_count; generic_fn_type_id->generic_params = allocate(inline_arg_count); for (int call_i = 0; call_i < call_param_count; call_i += 1) { int proto_i = call_i + struct_node_1_or_0; AstNode *generic_param_decl_node = decl_node->data.fn_proto.params.at(proto_i); assert(generic_param_decl_node->type == NodeTypeParamDecl); bool is_inline = generic_param_decl_node->data.param_decl.is_inline; if (!is_inline) continue; AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type; TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner, child_context, *generic_param_type_node); if (expected_param_type->id == TypeTableEntryIdInvalid) { return expected_param_type; } AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i); TypeTableEntry *param_type = analyze_expression(g, import, parent_context, expected_param_type, *param_node); if (param_type->id == TypeTableEntryIdInvalid) { return param_type; } // set child_context so that the previous param is in scope child_context = new_block_context(generic_param_decl_node, child_context); ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val; if (const_val->ok) { VariableTableEntry *var = add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, &generic_param_decl_node->data.param_decl.name, param_type, true, *param_node); // This generic function instance could be called with anything, so when this variable is read it // needs to know that it depends on compile time variable data. var->force_depends_on_compile_var = true; } else { add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression for inline parameter")); return g->builtin_types.entry_invalid; } GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[next_generic_param_index]; generic_param_value->type = param_type; generic_param_value->node = *param_node; next_generic_param_index += 1; } assert(next_generic_param_index == inline_arg_count); auto entry = g->generic_table.maybe_get(generic_fn_type_id); FnTableEntry *impl_fn; if (entry) { AstNode *impl_decl_node = entry->value; assert(impl_decl_node->type == NodeTypeFnProto); impl_fn = impl_decl_node->data.fn_proto.fn_table_entry; } else { AstNode *decl_node = generic_fn_type_id->decl_node; AstNode *impl_fn_def_node = ast_clone_subtree_special(decl_node->data.fn_proto.fn_def_node, &g->next_node_index, AstCloneSpecialOmitInlineParams); AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto; impl_decl_node->data.fn_proto.inline_arg_count = 0; impl_decl_node->data.fn_proto.generic_proto_node = decl_node; preview_fn_proto_instance(g, import, impl_decl_node, child_context); g->generic_table.put(generic_fn_type_id, impl_decl_node); impl_fn = impl_decl_node->data.fn_proto.fn_table_entry; } call_node->data.fn_call_expr.fn_entry = impl_fn; return analyze_fn_call_ptr(g, import, parent_context, expected_type, call_node, impl_fn->type_entry, struct_node); } static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *generic_fn_type) { assert(node->type == NodeTypeFnCallExpr); assert(generic_fn_type->id == TypeTableEntryIdGenericFn); AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node; assert(decl_node->type == NodeTypeContainerDecl); ZigList *generic_params = &decl_node->data.struct_decl.generic_params; int expected_param_count = generic_params->length; int actual_param_count = node->data.fn_call_expr.params.length; if (actual_param_count != expected_param_count) { add_node_error(g, first_executing_node(node), buf_sprintf("expected %d arguments, got %d", expected_param_count, actual_param_count)); return g->builtin_types.entry_invalid; } GenericFnTypeId *generic_fn_type_id = allocate(1); generic_fn_type_id->decl_node = decl_node; generic_fn_type_id->generic_param_count = actual_param_count; generic_fn_type_id->generic_params = allocate(actual_param_count); BlockContext *child_context = decl_node->owner->block_context; for (int i = 0; i < actual_param_count; i += 1) { AstNode *generic_param_decl_node = generic_params->at(i); assert(generic_param_decl_node->type == NodeTypeParamDecl); AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type; TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner, child_context, *generic_param_type_node); if (expected_param_type->id == TypeTableEntryIdInvalid) { return expected_param_type; } AstNode **param_node = &node->data.fn_call_expr.params.at(i); TypeTableEntry *param_type = analyze_expression(g, import, parent_context, expected_param_type, *param_node); if (param_type->id == TypeTableEntryIdInvalid) { return param_type; } // set child_context so that the previous param is in scope child_context = new_block_context(generic_param_decl_node, child_context); ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val; if (const_val->ok) { VariableTableEntry *var = add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, &generic_param_decl_node->data.param_decl.name, param_type, true, *param_node); var->force_depends_on_compile_var = true; } else { add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[i]; generic_param_value->type = param_type; generic_param_value->node = *param_node; } auto entry = g->generic_table.maybe_get(generic_fn_type_id); if (entry) { AstNode *impl_decl_node = entry->value; assert(impl_decl_node->type == NodeTypeContainerDecl); TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry; return resolve_expr_const_val_as_type(g, node, type_entry, false); } // make a type from the generic parameters supplied assert(decl_node->type == NodeTypeContainerDecl); AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index); g->generic_table.put(generic_fn_type_id, impl_decl_node); scan_struct_decl(g, import, child_context, impl_decl_node); TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry; resolve_struct_type(g, import, type_entry); return resolve_expr_const_val_as_type(g, node, type_entry, false); } static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; if (node->data.fn_call_expr.is_builtin) { return analyze_builtin_fn_call_expr(g, import, context, expected_type, node); } if (fn_ref_expr->type == NodeTypeFieldAccessExpr) { fn_ref_expr->data.field_access_expr.is_fn_call = true; } TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr); if (invoke_type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } // use constant expression evaluator to figure out the function at compile time. // otherwise we treat this as a function pointer. ConstExprValue *const_val = &get_resolved_expr(fn_ref_expr)->const_val; if (const_val->ok) { if (invoke_type_entry->id == TypeTableEntryIdMetaType) { return analyze_cast_expr(g, import, context, node); } else if (invoke_type_entry->id == TypeTableEntryIdFn) { AstNode *struct_node; if (fn_ref_expr->type == NodeTypeFieldAccessExpr && fn_ref_expr->data.field_access_expr.is_member_fn) { struct_node = fn_ref_expr->data.field_access_expr.struct_expr; } else { struct_node = nullptr; } FnTableEntry *fn_table_entry = const_val->data.x_fn; node->data.fn_call_expr.fn_entry = fn_table_entry; return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_node); } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) { TypeTableEntry *generic_fn_type = const_val->data.x_type; AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node; if (decl_node->type == NodeTypeFnProto) { AstNode *struct_node; if (fn_ref_expr->type == NodeTypeFieldAccessExpr && fn_ref_expr->data.field_access_expr.is_member_fn) { struct_node = fn_ref_expr->data.field_access_expr.struct_expr; } else { struct_node = nullptr; } FnTableEntry *fn_table_entry = decl_node->data.fn_proto.fn_table_entry; if (fn_table_entry->proto_node->data.fn_proto.skip) { return g->builtin_types.entry_invalid; } return analyze_fn_call_with_inline_args(g, import, context, expected_type, node, fn_table_entry, struct_node); } else { return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type); } } else { add_node_error(g, fn_ref_expr, buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name))); return g->builtin_types.entry_invalid; } } // function pointer if (invoke_type_entry->id == TypeTableEntryIdFn) { return analyze_fn_call_ptr(g, import, context, expected_type, node, invoke_type_entry, nullptr); } else { add_node_error(g, fn_ref_expr, buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name))); return g->builtin_types.entry_invalid; } } static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; AstNode **expr_node = &node->data.prefix_op_expr.primary_expr; switch (prefix_op) { case PrefixOpInvalid: zig_unreachable(); case PrefixOpBoolNot: { TypeTableEntry *type_entry = analyze_expression(g, import, context, g->builtin_types.entry_bool, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_bool; } ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val; if (!target_const_val->ok) { return g->builtin_types.entry_bool; } bool answer = !target_const_val->data.x_bool; return resolve_expr_const_val_as_bool(g, node, answer, target_const_val->depends_on_compile_var); } case PrefixOpBinNot: { TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, *expr_node); if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; } else if (expr_type->id == TypeTableEntryIdInt || expr_type->id == TypeTableEntryIdNumLitInt) { return expr_type; } else { add_node_error(g, *expr_node, buf_sprintf("invalid binary not type: '%s'", buf_ptr(&expr_type->name))); return g->builtin_types.entry_invalid; } // TODO const expr eval } case PrefixOpNegation: { TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node); if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; } else if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) || expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitInt || expr_type->id == TypeTableEntryIdNumLitFloat) { ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val; if (!target_const_val->ok) { return expr_type; } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; const_val->ok = true; const_val->depends_on_compile_var = target_const_val->depends_on_compile_var; bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum); return expr_type; } else { add_node_error(g, node, buf_sprintf("invalid negation type: '%s'", buf_ptr(&expr_type->name))); return g->builtin_types.entry_invalid; } } case PrefixOpAddressOf: case PrefixOpConstAddressOf: { bool is_const = (prefix_op == PrefixOpConstAddressOf); TypeTableEntry *child_type = analyze_lvalue(g, import, context, *expr_node, LValPurposeAddressOf, is_const); if (child_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (child_type->id == TypeTableEntryIdMetaType) { TypeTableEntry *meta_type = analyze_type_expr_pointer_only(g, import, context, *expr_node, true); if (meta_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (meta_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, node, buf_create_from_str("pointer to unreachable not allowed")); return g->builtin_types.entry_invalid; } else { return resolve_expr_const_val_as_type(g, node, get_pointer_to_type(g, meta_type, is_const), false); } } else if (child_type->id == TypeTableEntryIdNumLitInt || child_type->id == TypeTableEntryIdNumLitFloat) { add_node_error(g, *expr_node, buf_sprintf("unable to get address of type '%s'", buf_ptr(&child_type->name))); return g->builtin_types.entry_invalid; } else { return get_pointer_to_type(g, child_type, is_const); } } case PrefixOpDereference: { TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdPointer) { return type_entry->data.pointer.child_type; } else { add_node_error(g, *expr_node, buf_sprintf("indirection requires pointer operand ('%s' invalid)", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } } case PrefixOpMaybe: { TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdMetaType) { TypeTableEntry *meta_type = resolve_type(g, *expr_node); if (meta_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (meta_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in maybe type")); return g->builtin_types.entry_invalid; } else { return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type), false); } } else if (type_entry->id == TypeTableEntryIdUnreachable) { add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in maybe type")); return g->builtin_types.entry_invalid; } else { ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val; TypeTableEntry *maybe_type = get_maybe_type(g, type_entry); if (!target_const_val->ok) { return maybe_type; } return resolve_expr_const_val_as_non_null(g, node, maybe_type, target_const_val); } } case PrefixOpError: { TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdMetaType) { TypeTableEntry *meta_type = resolve_type(g, *expr_node); if (meta_type->id == TypeTableEntryIdInvalid) { return meta_type; } else if (meta_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in error type")); return g->builtin_types.entry_invalid; } else { return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type), false); } } else if (type_entry->id == TypeTableEntryIdUnreachable) { add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in error type")); return g->builtin_types.entry_invalid; } else { // TODO eval const expr return get_error_type(g, type_entry); } } case PrefixOpUnwrapError: { TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdErrorUnion) { return type_entry->data.error.child_type; } else { add_node_error(g, *expr_node, buf_sprintf("expected error type, got '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } } case PrefixOpUnwrapMaybe: { TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdMaybe) { return type_entry->data.maybe.child_type; } else { add_node_error(g, *expr_node, buf_sprintf("expected maybe type, got '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } } } zig_unreachable(); } static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNode **expr_node = &node->data.switch_expr.expr; TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node); ConstExprValue *expr_val = &get_resolved_expr(*expr_node)->const_val; if (expr_val->ok && !expr_val->depends_on_compile_var) { add_node_error(g, first_executing_node(*expr_node), buf_sprintf("value is constant; unnecessary switch statement")); } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; int prong_count = node->data.switch_expr.prongs.length; AstNode **peer_nodes = allocate(prong_count); TypeTableEntry **peer_types = allocate(prong_count); bool any_errors = false; if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; } else if (expr_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, first_executing_node(*expr_node), buf_sprintf("switch on unreachable expression not allowed")); return g->builtin_types.entry_invalid; } int *field_use_counts = nullptr; HashMap err_use_nodes; if (expr_type->id == TypeTableEntryIdEnum) { field_use_counts = allocate(expr_type->data.enumeration.field_count); } else if (expr_type->id == TypeTableEntryIdErrorUnion) { err_use_nodes.init(10); } int *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index; *const_chosen_prong_index = -1; AstNode *else_prong = nullptr; for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); TypeTableEntry *var_type; bool var_is_target_expr; if (prong_node->data.switch_prong.items.length == 0) { if (else_prong) { add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression")); any_errors = true; } else { else_prong = prong_node; } var_type = expr_type; var_is_target_expr = true; if (*const_chosen_prong_index == -1 && expr_val->ok) { *const_chosen_prong_index = prong_i; } } else { bool all_agree_on_var_type = true; var_type = nullptr; for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); if (item_node->type == NodeTypeSwitchRange) { zig_panic("TODO range in switch statement"); } if (expr_type->id == TypeTableEntryIdEnum) { if (item_node->type == NodeTypeSymbol) { Buf *field_name = &item_node->data.symbol_expr.symbol; TypeEnumField *type_enum_field = get_enum_field(expr_type, field_name); if (type_enum_field) { item_node->data.symbol_expr.enum_field = type_enum_field; if (!var_type) { var_type = type_enum_field->type_entry; } if (type_enum_field->type_entry != var_type) { all_agree_on_var_type = false; } uint32_t field_index = type_enum_field->value; assert(field_use_counts); field_use_counts[field_index] += 1; if (field_use_counts[field_index] > 1) { add_node_error(g, item_node, buf_sprintf("duplicate switch value: '%s'", buf_ptr(type_enum_field->name))); any_errors = true; } if (!any_errors && expr_val->ok) { if (expr_val->data.x_enum.tag == type_enum_field->value) { *const_chosen_prong_index = prong_i; } } } else { add_node_error(g, item_node, buf_sprintf("enum '%s' has no field '%s'", buf_ptr(&expr_type->name), buf_ptr(field_name))); any_errors = true; } } else { add_node_error(g, item_node, buf_sprintf("expected enum tag name")); any_errors = true; } } else if (expr_type->id == TypeTableEntryIdErrorUnion) { if (item_node->type == NodeTypeSymbol) { Buf *err_name = &item_node->data.symbol_expr.symbol; bool is_ok_case = buf_eql_str(err_name, "Ok"); auto err_table_entry = is_ok_case ? nullptr: g->error_table.maybe_get(err_name); if (is_ok_case || err_table_entry) { uint32_t err_value = is_ok_case ? 0 : err_table_entry->value->value; item_node->data.symbol_expr.err_value = err_value; TypeTableEntry *this_var_type; if (is_ok_case) { this_var_type = expr_type->data.error.child_type; } else { this_var_type = g->builtin_types.entry_pure_error; } if (!var_type) { var_type = this_var_type; } if (this_var_type != var_type) { all_agree_on_var_type = false; } // detect duplicate switch values auto existing_entry = err_use_nodes.maybe_get(err_value); if (existing_entry) { add_node_error(g, existing_entry->value, buf_sprintf("duplicate switch value: '%s'", buf_ptr(err_name))); any_errors = true; } else { err_use_nodes.put(err_value, item_node); } if (!any_errors && expr_val->ok) { if (expr_val->data.x_err.err->value == err_value) { *const_chosen_prong_index = prong_i; } } } else { add_node_error(g, item_node, buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name))); any_errors = true; } } else { add_node_error(g, item_node, buf_sprintf("expected error value name")); any_errors = true; } } else { if (!any_errors && expr_val->ok) { // note: there is now a function in eval.cpp for doing const expr comparison zig_panic("TODO determine if const exprs are equal"); } TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node); if (item_type->id != TypeTableEntryIdInvalid) { ConstExprValue *const_val = &get_resolved_expr(item_node)->const_val; if (!const_val->ok) { add_node_error(g, item_node, buf_sprintf("unable to evaluate constant expression")); any_errors = true; } } } } if (!var_type || !all_agree_on_var_type) { var_type = expr_type; var_is_target_expr = true; } else { var_is_target_expr = false; } } BlockContext *child_context = new_block_context(node, context); prong_node->data.switch_prong.block_context = child_context; AstNode *var_node = prong_node->data.switch_prong.var_symbol; if (var_node) { assert(var_node->type == NodeTypeSymbol); Buf *var_name = &var_node->data.symbol_expr.symbol; var_node->block_context = child_context; prong_node->data.switch_prong.var = add_local_var(g, var_node, import, child_context, var_name, var_type, true, nullptr); prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr; } } for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); BlockContext *child_context = prong_node->data.switch_prong.block_context; child_context->codegen_excluded = expr_val->ok && (*const_chosen_prong_index != prong_i); peer_nodes[prong_i] = prong_node->data.switch_prong.expr; if (child_context->codegen_excluded) { peer_types[prong_i] = g->builtin_types.entry_unreachable; } else { peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type, prong_node->data.switch_prong.expr); } } if (expr_type->id == TypeTableEntryIdEnum && !else_prong) { for (uint32_t i = 0; i < expr_type->data.enumeration.field_count; i += 1) { if (field_use_counts[i] == 0) { add_node_error(g, node, buf_sprintf("enumeration value '%s' not handled in switch", buf_ptr(expr_type->data.enumeration.fields[i].name))); any_errors = true; } } } if (any_errors) { return g->builtin_types.entry_invalid; } if (prong_count == 0) { add_node_error(g, node, buf_sprintf("switch statement has no prongs")); return g->builtin_types.entry_invalid; } TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count); if (expr_val->ok) { assert(*const_chosen_prong_index != -1); *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val; // the target expr depends on a compile var because we have an error on unnecessary // switch statement, so the entire switch statement does too const_val->depends_on_compile_var = true; if (!const_val->ok) { return add_error_if_type_is_num_lit(g, result_type, node); } } else { return add_error_if_type_is_num_lit(g, result_type, node); } return result_type; } static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { if (!context->fn_entry) { add_node_error(g, node, buf_sprintf("return expression outside function definition")); return g->builtin_types.entry_invalid; } if (!node->data.return_expr.expr) { node->data.return_expr.expr = create_ast_void_node(g, import, node); normalize_parent_ptrs(node); } TypeTableEntry *expected_return_type = get_return_type(context); switch (node->data.return_expr.kind) { case ReturnKindUnconditional: { analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr); return g->builtin_types.entry_unreachable; } case ReturnKindError: { TypeTableEntry *expected_err_type; if (expected_type) { expected_err_type = get_error_type(g, expected_type); } else { expected_err_type = nullptr; } TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_err_type, node->data.return_expr.expr); if (resolved_type->id == TypeTableEntryIdInvalid) { return resolved_type; } else if (resolved_type->id == TypeTableEntryIdErrorUnion) { TypeTableEntry *return_type = context->fn_entry->type_entry->data.fn.fn_type_id.return_type; if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) { ErrorMsg *msg = add_node_error(g, node, buf_sprintf("%%return statement in function with return type '%s'", buf_ptr(&return_type->name))); AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type; add_error_note(g, msg, return_type_node, buf_sprintf("function return type here")); } return resolved_type->data.error.child_type; } else { add_node_error(g, node->data.return_expr.expr, buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name))); return g->builtin_types.entry_invalid; } } case ReturnKindMaybe: zig_panic("TODO"); } zig_unreachable(); } static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdMetaType) { add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type")); } else if (type_entry->id == TypeTableEntryIdErrorUnion) { add_node_error(g, first_executing_node(source_node), buf_sprintf("statement ignores error value")); } } static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) { if (!parent_context->fn_entry) { add_node_error(g, node, buf_sprintf("defer expression outside function definition")); return g->builtin_types.entry_invalid; } if (!node->data.defer.expr) { add_node_error(g, node, buf_sprintf("defer expects an expression")); return g->builtin_types.entry_void; } node->data.defer.child_block = new_block_context(node, parent_context); TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr, node->data.defer.expr); validate_voided_expr(g, node->data.defer.expr, resolved_type); return g->builtin_types.entry_void; } static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { if (node->data.string_literal.c) { return resolve_expr_const_val_as_c_string_lit(g, node, &node->data.string_literal.buf); } else { return resolve_expr_const_val_as_string_lit(g, node, &node->data.string_literal.buf); } } static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) { BlockContext *child_context = new_block_context(node, parent_context); node->data.block.child_block = child_context; TypeTableEntry *return_type = g->builtin_types.entry_void; for (int i = 0; i < node->data.block.statements.length; i += 1) { AstNode *child = node->data.block.statements.at(i); if (child->type == NodeTypeLabel) { FnTableEntry *fn_table_entry = child_context->fn_entry; assert(fn_table_entry); LabelTableEntry *label = allocate(1); label->decl_node = child; label->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable); child->block_context = child_context; child->data.label.label_entry = label; fn_table_entry->all_labels.append(label); child_context->label_table.put(&child->data.label.name, label); return_type = g->builtin_types.entry_void; continue; } if (return_type->id == TypeTableEntryIdUnreachable) { if (is_node_void_expr(child)) { // {unreachable;void;void} is allowed. // ignore void statements once we enter unreachable land. analyze_expression(g, import, child_context, g->builtin_types.entry_void, child); continue; } add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code")); break; } bool is_last = (i == node->data.block.statements.length - 1); TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; return_type = analyze_expression(g, import, child_context, passed_expected_type, child); if (child->type == NodeTypeDefer && return_type->id != TypeTableEntryIdInvalid) { // defer starts a new block context child_context = child->data.defer.child_block; assert(child_context); } if (!is_last) { validate_voided_expr(g, child, return_type); } } node->data.block.nested_block = child_context; ConstExprValue *const_val = &node->data.block.resolved_expr.const_val; if (node->data.block.statements.length == 0) { const_val->ok = true; } else if (node->data.block.statements.length == 1) { AstNode *only_node = node->data.block.statements.at(0); ConstExprValue *other_const_val = &get_resolved_expr(only_node)->const_val; if (other_const_val->ok) { *const_val = *other_const_val; } } return return_type; } static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { mark_impure_fn(context); node->data.asm_expr.return_count = 0; TypeTableEntry *return_type = g->builtin_types.entry_void; for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); if (asm_output->return_type) { node->data.asm_expr.return_count += 1; return_type = analyze_type_expr(g, import, context, asm_output->return_type); if (node->data.asm_expr.return_count > 1) { add_node_error(g, node, buf_sprintf("inline assembly allows up to one output value")); break; } } else { Buf *variable_name = &asm_output->variable_name; VariableTableEntry *var = find_variable(g, context, variable_name); if (var) { asm_output->variable = var; return var->type; } else { add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); return g->builtin_types.entry_invalid; } } } for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) { AsmInput *asm_input = node->data.asm_expr.input_list.at(i); analyze_expression(g, import, context, nullptr, asm_input->expr); } return return_type; } static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeGoto); FnTableEntry *fn_table_entry = context->fn_entry; assert(fn_table_entry); fn_table_entry->goto_list.append(node); return g->builtin_types.entry_unreachable; } static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeGoto); Buf *label_name = &node->data.goto_expr.name; BlockContext *context = node->block_context; assert(context); LabelTableEntry *label = find_label(g, context, label_name); if (!label) { add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name))); return; } label->used = true; node->data.goto_expr.label_entry = label; } static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid); TypeTableEntry *return_type = nullptr; node->block_context = context; switch (node->type) { case NodeTypeBlock: return_type = analyze_block_expr(g, import, context, expected_type, node); break; case NodeTypeReturnExpr: return_type = analyze_return_expr(g, import, context, expected_type, node); break; case NodeTypeDefer: return_type = analyze_defer(g, import, context, expected_type, node); break; case NodeTypeVariableDeclaration: analyze_variable_declaration(g, import, context, expected_type, node); return_type = g->builtin_types.entry_void; break; case NodeTypeGoto: return_type = analyze_goto_pass1(g, import, context, expected_type, node); break; case NodeTypeBreak: return_type = analyze_break_expr(g, import, context, expected_type, node); break; case NodeTypeContinue: return_type = analyze_continue_expr(g, import, context, expected_type, node); break; case NodeTypeAsmExpr: return_type = analyze_asm_expr(g, import, context, expected_type, node); break; case NodeTypeBinOpExpr: return_type = analyze_bin_op_expr(g, import, context, expected_type, node); break; case NodeTypeUnwrapErrorExpr: return_type = analyze_unwrap_error_expr(g, import, context, expected_type, node); break; case NodeTypeFnCallExpr: return_type = analyze_fn_call_expr(g, import, context, expected_type, node); break; case NodeTypeArrayAccessExpr: // for reading array access; assignment handled elsewhere return_type = analyze_array_access_expr(g, import, context, node); break; case NodeTypeSliceExpr: return_type = analyze_slice_expr(g, import, context, node); break; case NodeTypeFieldAccessExpr: return_type = analyze_field_access_expr(g, import, context, expected_type, node); break; case NodeTypeContainerInitExpr: return_type = analyze_container_init_expr(g, import, context, node); break; case NodeTypeNumberLiteral: return_type = analyze_number_literal_expr(g, import, context, expected_type, node); break; case NodeTypeStringLiteral: return_type = analyze_string_literal_expr(g, import, context, expected_type, node); break; case NodeTypeCharLiteral: return_type = resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, node->data.char_literal.value, false); break; case NodeTypeBoolLiteral: return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value, false); break; case NodeTypeNullLiteral: return_type = analyze_null_literal_expr(g, import, context, expected_type, node); break; case NodeTypeUndefinedLiteral: return_type = analyze_undefined_literal_expr(g, import, context, expected_type, node); break; case NodeTypeSymbol: return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only); break; case NodeTypePrefixOpExpr: return_type = analyze_prefix_op_expr(g, import, context, expected_type, node); break; case NodeTypeIfBoolExpr: return_type = analyze_if_bool_expr(g, import, context, expected_type, node); break; case NodeTypeIfVarExpr: return_type = analyze_if_var_expr(g, import, context, expected_type, node); break; case NodeTypeWhileExpr: return_type = analyze_while_expr(g, import, context, expected_type, node); break; case NodeTypeForExpr: return_type = analyze_for_expr(g, import, context, expected_type, node); break; case NodeTypeArrayType: return_type = analyze_array_type(g, import, context, expected_type, node); break; case NodeTypeFnProto: return_type = analyze_fn_proto_expr(g, import, context, expected_type, node); break; case NodeTypeErrorType: return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_pure_error, false); break; case NodeTypeTypeLiteral: return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_type, false); break; case NodeTypeSwitchExpr: return_type = analyze_switch_expr(g, import, context, expected_type, node); break; case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeDirective: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeRoot: case NodeTypeFnDef: case NodeTypeUse: case NodeTypeLabel: case NodeTypeContainerDecl: case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeErrorValueDecl: case NodeTypeTypeDecl: zig_unreachable(); } assert(return_type); // resolve_type_compatibility might do implicit cast which means node is now a child // of the actual node that we want to return the type of. //AstNode **field = node->parent_field; TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, context, node, expected_type, return_type); Expr *expr = get_resolved_expr(node); expr->type_entry = return_type; add_global_const_expr(g, node); return resolved_type; } // When you call analyze_expression, the node you pass might no longer be the child node // you thought it was due to implicit casting rewriting the AST. static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { return analyze_expression_pointer_only(g, import, context, expected_type, node, false); } static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { ImportTableEntry *import = fn_table_entry->import_entry; AstNode *node = fn_table_entry->fn_def_node; assert(node->type == NodeTypeFnDef); AstNode *fn_proto_node = node->data.fn_def.fn_proto; assert(fn_proto_node->type == NodeTypeFnProto); if (fn_proto_node->data.fn_proto.skip) { // we detected an error with this function definition which prevents us // from further analyzing it. fn_table_entry->anal_state = FnAnalStateSkipped; return; } fn_table_entry->anal_state = FnAnalStateProbing; BlockContext *context = node->data.fn_def.block_context; TypeTableEntry *fn_type = fn_table_entry->type_entry; AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; for (int i = 0; i < fn_proto->params.length; i += 1) { AstNode *param_decl_node = fn_proto->params.at(i); assert(param_decl_node->type == NodeTypeParamDecl); // define local variables for parameters AstNodeParamDecl *param_decl = ¶m_decl_node->data.param_decl; TypeTableEntry *type = unwrapped_node_type(param_decl->type); if (param_decl->is_noalias && !type_is_codegen_pointer(type)) { add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); } if (fn_type->data.fn.fn_type_id.is_extern && type->id == TypeTableEntryIdStruct) { add_node_error(g, param_decl_node, buf_sprintf("byvalue struct parameters not yet supported on extern functions")); } if (buf_len(¶m_decl->name) == 0) { add_node_error(g, param_decl_node, buf_sprintf("missing parameter name")); } VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, ¶m_decl->name, type, true, nullptr); var->src_arg_index = i; param_decl_node->data.param_decl.variable = var; if (fn_type->data.fn.gen_param_info) { var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; } if (!type->deep_const) { fn_table_entry->is_pure = false; } } TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type; TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body); node->data.fn_def.implicit_return_type = block_return_type; for (int i = 0; i < fn_table_entry->goto_list.length; i += 1) { AstNode *goto_node = fn_table_entry->goto_list.at(i); assert(goto_node->type == NodeTypeGoto); analyze_goto_pass2(g, import, goto_node); } for (int i = 0; i < fn_table_entry->all_labels.length; i += 1) { LabelTableEntry *label = fn_table_entry->all_labels.at(i); if (!label->used) { add_node_error(g, label->decl_node, buf_sprintf("label '%s' defined but not used", buf_ptr(&label->decl_node->data.label.name))); } } fn_table_entry->anal_state = FnAnalStateComplete; } static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, AstNode *node, Buf *name) { assert(import); TopLevelDecl *tld = get_as_top_level_decl(node); tld->import = import; tld->name = name; bool want_to_resolve = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport); bool is_generic_container = (node->type == NodeTypeContainerDecl && node->data.struct_decl.generic_params.length > 0); if (want_to_resolve && !is_generic_container) { g->resolve_queue.append(node); } node->block_context = block_context; auto entry = block_context->decl_table.maybe_get(name); if (entry) { AstNode *other_decl_node = entry->value; ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, other_decl_node, buf_sprintf("previous definition is here")); } else { block_context->decl_table.put(name, node); } } static int fn_proto_inline_arg_count(AstNode *proto_node) { assert(proto_node->type == NodeTypeFnProto); int result = 0; for (int i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { AstNode *param_node = proto_node->data.fn_proto.params.at(i); assert(param_node->type == NodeTypeParamDecl); result += param_node->data.param_decl.is_inline ? 1 : 0; } return result; } static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { switch (node->type) { case NodeTypeRoot: for (int i = 0; i < import->root->data.root.top_level_decls.length; i += 1) { AstNode *child = import->root->data.root.top_level_decls.at(i); scan_decls(g, import, context, child); } break; case NodeTypeContainerDecl: { Buf *name = &node->data.struct_decl.name; add_top_level_decl(g, import, context, node, name); if (node->data.struct_decl.generic_params.length == 0) { scan_struct_decl(g, import, context, node); } } break; case NodeTypeFnDef: node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node; scan_decls(g, import, context, node->data.fn_def.fn_proto); break; case NodeTypeVariableDeclaration: { Buf *name = &node->data.variable_declaration.symbol; add_top_level_decl(g, import, context, node, name); break; } case NodeTypeTypeDecl: { Buf *name = &node->data.type_decl.symbol; add_top_level_decl(g, import, context, node, name); break; } case NodeTypeFnProto: { // if the name is missing, we immediately announce an error Buf *fn_name = &node->data.fn_proto.name; if (buf_len(fn_name) == 0) { node->data.fn_proto.skip = true; add_node_error(g, node, buf_sprintf("missing function name")); break; } node->data.fn_proto.inline_arg_count = fn_proto_inline_arg_count(node); add_top_level_decl(g, import, context, node, fn_name); break; } case NodeTypeUse: { TopLevelDecl *tld = get_as_top_level_decl(node); tld->import = import; node->block_context = context; g->use_queue.append(node); tld->import->use_decls.append(node); break; } case NodeTypeErrorValueDecl: // error value declarations do not depend on other top level decls preview_error_value_decl(g, node); break; case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeBlock: case NodeTypeBinOpExpr: case NodeTypeUnwrapErrorExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: case NodeTypeContainerInitExpr: case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeTypeLiteral: zig_unreachable(); } } static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) { TopLevelDecl *tld = get_as_top_level_decl(dst_use_node); AstNode *use_target_node = src_use_node->data.use.expr; Expr *expr = get_resolved_expr(use_target_node); if (expr->type_entry->id == TypeTableEntryIdInvalid) { return; } tld->resolution = TldResolutionOk; ConstExprValue *const_val = &expr->const_val; assert(const_val->ok); ImportTableEntry *target_import = const_val->data.x_import; assert(target_import); if (target_import->any_imports_failed) { tld->import->any_imports_failed = true; } for (int i = 0; i < target_import->root->data.root.top_level_decls.length; i += 1) { AstNode *decl_node = target_import->root->data.root.top_level_decls.at(i); if (decl_node->type == NodeTypeFnDef) { decl_node = decl_node->data.fn_def.fn_proto; } TopLevelDecl *target_tld = get_as_top_level_decl(decl_node); if (!target_tld->name) { continue; } if (target_tld->visib_mod != VisibModPrivate) { auto existing_entry = tld->import->block_context->decl_table.maybe_get(target_tld->name); if (existing_entry) { AstNode *existing_decl = existing_entry->value; if (existing_decl != decl_node) { 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, buf_sprintf("previous definition here")); add_error_note(g, msg, decl_node, buf_sprintf("imported definition here")); } } else { tld->import->block_context->decl_table.put(target_tld->name, decl_node); } } } for (int i = 0; i < target_import->use_decls.length; i += 1) { AstNode *use_decl_node = target_import->use_decls.at(i); TopLevelDecl *target_tld = get_as_top_level_decl(use_decl_node); if (target_tld->visib_mod != VisibModPrivate) { add_symbols_from_import(g, use_decl_node, dst_use_node); } } } static void resolve_use_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeUse); if (get_as_top_level_decl(node)->resolution != TldResolutionUnresolved) { return; } add_symbols_from_import(g, node, node); } static void preview_use_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeUse); TopLevelDecl *tld = get_as_top_level_decl(node); TypeTableEntry *use_expr_type = analyze_expression(g, tld->import, tld->import->block_context, g->builtin_types.entry_namespace, node->data.use.expr); if (use_expr_type->id == TypeTableEntryIdInvalid) { tld->import->any_imports_failed = true; } } ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code) { Buf *full_path = buf_alloc(); os_path_join(src_dirname, src_basename, full_path); if (g->verbose) { fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(full_path)); fprintf(stderr, "----------------\n"); fprintf(stderr, "%s\n", buf_ptr(source_code)); fprintf(stderr, "\nTokens:\n"); fprintf(stderr, "---------\n"); } Tokenization tokenization = {0}; tokenize(source_code, &tokenization); if (tokenization.err) { ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column, source_code, tokenization.line_offsets, tokenization.err); print_err_msg(err, g->err_color); exit(1); } if (g->verbose) { print_tokens(source_code, tokenization.tokens); fprintf(stderr, "\nAST:\n"); fprintf(stderr, "------\n"); } ImportTableEntry *import_entry = allocate(1); import_entry->package = package; import_entry->source_code = source_code; import_entry->line_offsets = tokenization.line_offsets; import_entry->path = full_path; import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color, &g->next_node_index); assert(import_entry->root); if (g->verbose) { ast_print(stderr, import_entry->root, 0); } import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); g->import_table.put(abs_full_path, import_entry); g->import_queue.append(import_entry); import_entry->block_context = new_block_context(import_entry->root, nullptr); import_entry->block_context->di_scope = LLVMZigFileToScope(import_entry->di_file); assert(import_entry->root->type == NodeTypeRoot); for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) { AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i); if (top_level_decl->type == NodeTypeFnDef) { AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; assert(proto_node->type == NodeTypeFnProto); Buf *proto_name = &proto_node->data.fn_proto.name; bool is_private = (proto_node->data.fn_proto.top_level_decl.visib_mod == VisibModPrivate); if (buf_eql_str(proto_name, "main") && !is_private) { g->have_exported_main = true; } } } return import_entry; } void semantic_analyze(CodeGen *g) { for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) { ImportTableEntry *import = g->import_queue.at(g->import_queue_index); scan_decls(g, import, import->block_context, import->root); } for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) { AstNode *use_decl_node = g->use_queue.at(g->use_queue_index); preview_use_decl(g, use_decl_node); } for (int i = 0; i < g->use_queue.length; i += 1) { AstNode *use_decl_node = g->use_queue.at(i); resolve_use_decl(g, use_decl_node); } for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) { AstNode *decl_node = g->resolve_queue.at(g->resolve_queue_index); bool pointer_only = false; resolve_top_level_decl(g, decl_node, pointer_only); } for (int i = 0; i < g->fn_defs.length; i += 1) { FnTableEntry *fn_entry = g->fn_defs.at(i); if (fn_entry->anal_state == FnAnalStateReady) { analyze_fn_body(g, fn_entry); } } } Expr *get_resolved_expr(AstNode *node) { switch (node->type) { case NodeTypeReturnExpr: return &node->data.return_expr.resolved_expr; case NodeTypeDefer: return &node->data.defer.resolved_expr; case NodeTypeBinOpExpr: return &node->data.bin_op_expr.resolved_expr; case NodeTypeUnwrapErrorExpr: return &node->data.unwrap_err_expr.resolved_expr; case NodeTypePrefixOpExpr: return &node->data.prefix_op_expr.resolved_expr; case NodeTypeFnCallExpr: return &node->data.fn_call_expr.resolved_expr; case NodeTypeArrayAccessExpr: return &node->data.array_access_expr.resolved_expr; case NodeTypeSliceExpr: return &node->data.slice_expr.resolved_expr; case NodeTypeFieldAccessExpr: return &node->data.field_access_expr.resolved_expr; case NodeTypeIfBoolExpr: return &node->data.if_bool_expr.resolved_expr; case NodeTypeIfVarExpr: return &node->data.if_var_expr.resolved_expr; case NodeTypeWhileExpr: return &node->data.while_expr.resolved_expr; case NodeTypeForExpr: return &node->data.for_expr.resolved_expr; case NodeTypeAsmExpr: return &node->data.asm_expr.resolved_expr; case NodeTypeContainerInitExpr: return &node->data.container_init_expr.resolved_expr; case NodeTypeNumberLiteral: return &node->data.number_literal.resolved_expr; case NodeTypeStringLiteral: return &node->data.string_literal.resolved_expr; case NodeTypeBlock: return &node->data.block.resolved_expr; case NodeTypeSymbol: return &node->data.symbol_expr.resolved_expr; case NodeTypeVariableDeclaration: return &node->data.variable_declaration.resolved_expr; case NodeTypeCharLiteral: return &node->data.char_literal.resolved_expr; case NodeTypeBoolLiteral: return &node->data.bool_literal.resolved_expr; case NodeTypeNullLiteral: return &node->data.null_literal.resolved_expr; case NodeTypeUndefinedLiteral: return &node->data.undefined_literal.resolved_expr; case NodeTypeGoto: return &node->data.goto_expr.resolved_expr; case NodeTypeBreak: return &node->data.break_expr.resolved_expr; case NodeTypeContinue: return &node->data.continue_expr.resolved_expr; case NodeTypeLabel: return &node->data.label.resolved_expr; case NodeTypeArrayType: return &node->data.array_type.resolved_expr; case NodeTypeErrorType: return &node->data.error_type.resolved_expr; case NodeTypeTypeLiteral: return &node->data.type_literal.resolved_expr; case NodeTypeSwitchExpr: return &node->data.switch_expr.resolved_expr; case NodeTypeFnProto: return &node->data.fn_proto.resolved_expr; case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeRoot: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeDirective: case NodeTypeUse: case NodeTypeContainerDecl: case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeErrorValueDecl: case NodeTypeTypeDecl: zig_unreachable(); } zig_unreachable(); } static TopLevelDecl *get_as_top_level_decl(AstNode *node) { switch (node->type) { case NodeTypeVariableDeclaration: return &node->data.variable_declaration.top_level_decl; case NodeTypeFnProto: return &node->data.fn_proto.top_level_decl; case NodeTypeFnDef: return &node->data.fn_def.fn_proto->data.fn_proto.top_level_decl; case NodeTypeContainerDecl: return &node->data.struct_decl.top_level_decl; case NodeTypeErrorValueDecl: return &node->data.error_value_decl.top_level_decl; case NodeTypeUse: return &node->data.use.top_level_decl; case NodeTypeTypeDecl: return &node->data.type_decl.top_level_decl; case NodeTypeNumberLiteral: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeBinOpExpr: case NodeTypeUnwrapErrorExpr: case NodeTypePrefixOpExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeFieldAccessExpr: case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeAsmExpr: case NodeTypeContainerInitExpr: case NodeTypeRoot: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeBlock: case NodeTypeDirective: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeSymbol: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeTypeLiteral: zig_unreachable(); } zig_unreachable(); } bool is_node_void_expr(AstNode *node) { if (node->type == NodeTypeContainerInitExpr && node->data.container_init_expr.kind == ContainerInitKindArray) { AstNode *type_node = node->data.container_init_expr.type; if (type_node->type == NodeTypeSymbol && buf_eql_str(&type_node->data.symbol_expr.symbol, "void")) { return true; } } return false; } TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits) { int index; if (size_in_bits == 8) { index = 0; } else if (size_in_bits == 16) { index = 1; } else if (size_in_bits == 32) { index = 2; } else if (size_in_bits == 64) { index = 3; } else { zig_unreachable(); } return &g->builtin_types.entry_int[is_signed ? 0 : 1][is_wrapping ? 0 : 1][index]; } TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits) { return *get_int_type_ptr(g, is_signed, is_wrapping, size_in_bits); } TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) { return &g->builtin_types.entry_c_int[c_int_type]; } TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type) { return *get_c_int_type_ptr(g, c_int_type); } bool handle_is_ptr(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: return false; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUnion: return true; case TypeTableEntryIdErrorUnion: return type_has_bits(type_entry->data.error.child_type); case TypeTableEntryIdEnum: return type_entry->data.enumeration.gen_field_count != 0; case TypeTableEntryIdMaybe: return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && type_entry->data.maybe.child_type->id != TypeTableEntryIdFn; case TypeTableEntryIdTypeDecl: return handle_is_ptr(type_entry->data.type_decl.canonical_type); } zig_unreachable(); } void find_libc_include_path(CodeGen *g) { if (!g->libc_include_dir || buf_len(g->libc_include_dir) == 0) { zig_panic("Unable to determine libc include path."); } } void find_libc_lib_path(CodeGen *g) { // later we can handle this better by reporting an error via the normal mechanism if (!g->libc_lib_dir || buf_len(g->libc_lib_dir) == 0) { zig_panic("Unable to determine libc lib path."); } if (!g->libc_static_lib_dir || buf_len(g->libc_static_lib_dir) == 0) { zig_panic("Unable to determine libc static lib path."); } } static uint32_t hash_ptr(void *ptr) { uint64_t x = (uint64_t)(uintptr_t)(ptr); uint32_t a = x >> 32; uint32_t b = x & 0xffffffff; return a ^ b; } uint32_t fn_type_id_hash(FnTypeId *id) { uint32_t result = 0; result += id->is_extern ? 3349388391 : 0; result += id->is_naked ? 608688877 : 0; result += id->is_cold ? 3605523458 : 0; result += id->is_var_args ? 1931444534 : 0; result += hash_ptr(id->return_type); for (int i = 0; i < id->param_count; i += 1) { FnTypeParamInfo *info = &id->param_info[i]; result += info->is_noalias ? 892356923 : 0; result += hash_ptr(info->type); } return result; } bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { if (a->is_extern != b->is_extern || a->is_naked != b->is_naked || a->is_cold != b->is_cold || a->return_type != b->return_type || a->is_var_args != b->is_var_args || a->param_count != b->param_count) { return false; } for (int 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(TypeTableEntry *type, ConstExprValue *const_val) { switch (type->id) { case TypeTableEntryIdBool: return const_val->data.x_bool ? 127863866 : 215080464; case TypeTableEntryIdMetaType: return hash_ptr(const_val->data.x_type); case TypeTableEntryIdVoid: return 4149439618; case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: return ((uint32_t)(bignum_to_twos_complement(&const_val->data.x_bignum) % UINT32_MAX)) * 1331471175; case TypeTableEntryIdFloat: case TypeTableEntryIdNumLitFloat: return const_val->data.x_bignum.data.x_float * UINT32_MAX; case TypeTableEntryIdPointer: return hash_ptr(const_val->data.x_ptr.ptr); case TypeTableEntryIdUndefLit: return 162837799; case TypeTableEntryIdArray: // TODO better hashing algorithm return 1166190605; case TypeTableEntryIdStruct: // TODO better hashing algorithm return 1532530855; case TypeTableEntryIdUnion: // TODO better hashing algorithm return 2709806591; case TypeTableEntryIdMaybe: if (const_val->data.x_maybe) { TypeTableEntry *child_type = type->data.maybe.child_type; return hash_const_val(child_type, const_val->data.x_maybe) * 1992916303; } else { return 4016830364; } case TypeTableEntryIdErrorUnion: // TODO better hashing algorithm return 3415065496; case TypeTableEntryIdPureError: // TODO better hashing algorithm return 2630160122; case TypeTableEntryIdEnum: // TODO better hashing algorithm return 31643936; case TypeTableEntryIdFn: return hash_ptr(const_val->data.x_fn); case TypeTableEntryIdTypeDecl: return hash_ptr(const_val->data.x_type); case TypeTableEntryIdNamespace: return hash_ptr(const_val->data.x_import); case TypeTableEntryIdGenericFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdUnreachable: zig_unreachable(); } zig_unreachable(); } uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) { uint32_t result = 0; result += hash_ptr(id->decl_node); for (int i = 0; i < id->generic_param_count; i += 1) { GenericParamValue *generic_param = &id->generic_params[i]; ConstExprValue *const_val = &get_resolved_expr(generic_param->node)->const_val; assert(const_val->ok); result += hash_const_val(generic_param->type, const_val); } return result; } bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) { if (a->decl_node != b->decl_node) return false; assert(a->generic_param_count == b->generic_param_count); for (int i = 0; i < a->generic_param_count; i += 1) { GenericParamValue *a_val = &a->generic_params[i]; GenericParamValue *b_val = &b->generic_params[i]; assert(a_val->type == b_val->type); ConstExprValue *a_const_val = &get_resolved_expr(a_val->node)->const_val; ConstExprValue *b_const_val = &get_resolved_expr(b_val->node)->const_val; assert(a_const_val->ok); assert(b_const_val->ok); if (!const_values_equal(a_const_val, b_const_val, a_val->type)) { return false; } } return true; } bool type_has_bits(TypeTableEntry *type_entry) { assert(type_entry); assert(type_entry->id != TypeTableEntryIdInvalid); return !type_entry->zero_bits; } static TypeTableEntry *first_struct_field_type(TypeTableEntry *type_entry) { assert(type_entry->id == TypeTableEntryIdStruct); for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { TypeStructField *tsf = &type_entry->data.structure.fields[i]; if (tsf->gen_index == 0) { return tsf->type_entry; } } zig_unreachable(); } static TypeTableEntry *type_of_first_thing_in_memory(TypeTableEntry *type_entry) { assert(type_has_bits(type_entry)); switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdUnreachable: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdNamespace: case TypeTableEntryIdGenericFn: zig_unreachable(); case TypeTableEntryIdArray: return type_of_first_thing_in_memory(type_entry->data.array.child_type); case TypeTableEntryIdStruct: return type_of_first_thing_in_memory(first_struct_field_type(type_entry)); case TypeTableEntryIdUnion: zig_panic("TODO"); case TypeTableEntryIdMaybe: return type_of_first_thing_in_memory(type_entry->data.maybe.child_type); case TypeTableEntryIdErrorUnion: return type_of_first_thing_in_memory(type_entry->data.error.child_type); case TypeTableEntryIdTypeDecl: return type_of_first_thing_in_memory(type_entry->data.type_decl.canonical_type); case TypeTableEntryIdEnum: return type_entry->data.enumeration.tag_type; case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: return type_entry; } zig_unreachable(); } uint64_t get_memcpy_align(CodeGen *g, TypeTableEntry *type_entry) { TypeTableEntry *first_type_in_mem = type_of_first_thing_in_memory(type_entry); return LLVMABISizeOfType(g->target_data_ref, first_type_in_mem->type_ref); }