diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-11-04 15:36:30 -0400 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-11-04 15:36:30 -0400 |
| commit | a2e32939305e470ce3d32c9d2667d3083158ddb3 (patch) | |
| tree | fef679349a0c2d2e25072e2960e39b2ec257fa07 /src | |
| parent | bc6c33b1b64822b0667ab88b73f4b5c4b302154f (diff) | |
| download | zig-a2e32939305e470ce3d32c9d2667d3083158ddb3.tar.gz zig-a2e32939305e470ce3d32c9d2667d3083158ddb3.zip | |
WIP moving all analysis to IR
Diffstat (limited to 'src')
| -rw-r--r-- | src/all_types.hpp | 18 | ||||
| -rw-r--r-- | src/analyze.cpp | 3517 | ||||
| -rw-r--r-- | src/analyze.hpp | 4 | ||||
| -rw-r--r-- | src/ast_render.cpp | 32 | ||||
| -rw-r--r-- | src/codegen.cpp | 4 | ||||
| -rw-r--r-- | src/ir.cpp | 3913 | ||||
| -rw-r--r-- | src/ir_print.cpp | 33 | ||||
| -rw-r--r-- | src/parseh.cpp | 4 |
8 files changed, 3430 insertions, 4095 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index 5495e25d39..8cc943f30b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -738,8 +738,6 @@ struct AstNodeSymbolExpr { // populated by semantic analyzer Expr resolved_expr; - // set this to instead of analyzing the node, pretend it's a type entry and it's this one. - TypeTableEntry *override_type_entry; TypeEnumField *enum_field; uint32_t err_value; }; @@ -1439,7 +1437,6 @@ enum IrInstructionId { IrInstructionIdElemPtr, IrInstructionIdVarPtr, IrInstructionIdCall, - IrInstructionIdBuiltinCall, IrInstructionIdConst, IrInstructionIdReturn, IrInstructionIdCast, @@ -1449,6 +1446,7 @@ enum IrInstructionId { IrInstructionIdTypeOf, IrInstructionIdToPtrType, IrInstructionIdPtrTypeChild, + IrInstructionIdSetFnTest, }; struct IrInstruction { @@ -1630,13 +1628,6 @@ struct IrInstructionCall { IrInstruction **args; }; -struct IrInstructionBuiltinCall { - IrInstruction base; - - BuiltinFnEntry *fn; - IrInstruction **args; -}; - struct IrInstructionConst { IrInstruction base; }; @@ -1698,6 +1689,13 @@ struct IrInstructionPtrTypeChild { IrInstruction *value; }; +struct IrInstructionSetFnTest { + IrInstruction base; + + IrInstruction *fn_value; + IrInstruction *is_test; +}; + enum LValPurpose { LValPurposeNone, LValPurposeAssign, diff --git a/src/analyze.cpp b/src/analyze.cpp index 8a0fe19098..50e690b9bf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -17,41 +17,9 @@ #include "parser.hpp" #include "zig_llvm.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_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type); static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type); -static TypeTableEntry *unwrapped_node_type(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_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 TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, - bool pointer_only, BlockContext *block_context, bool depends_on_compile_var); -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 VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import, - BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, - AstNode *val_node); AstNode *first_executing_node(AstNode *node) { switch (node->type) { @@ -868,39 +836,52 @@ TypeTableEntry *get_underlying_type(TypeTableEntry *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; +static IrInstruction *analyze_const_value(CodeGen *g, BlockContext *scope, AstNode *node, + TypeTableEntry *expected_type) +{ + IrExecutable ir_executable = {0}; + IrExecutable analyzed_executable = {0}; + IrInstruction *pass1 = ir_gen(g, node, scope, &ir_executable); + + if (pass1->type_entry->id == TypeTableEntryIdInvalid) + return g->invalid_instruction; + + if (g->verbose) { + fprintf(stderr, "{\n"); + ir_print(stderr, &ir_executable, 4); + fprintf(stderr, "}\n"); } - 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; + TypeTableEntry *result_type = ir_analyze(g, &ir_executable, &analyzed_executable, expected_type, node); + if (result_type->id == TypeTableEntryIdInvalid) + return g->invalid_instruction; + + if (g->verbose) { + fprintf(stderr, "{ // (analyzed)\n"); + ir_print(stderr, &analyzed_executable, 4); + fprintf(stderr, "}\n"); } - ConstExprValue *const_val = &expr->const_val; - if (!const_val->ok) { + IrInstruction *result = ir_exec_const_result(&analyzed_executable); + if (!result) { add_node_error(g, node, buf_sprintf("unable to evaluate constant expression")); - return g->builtin_types.entry_invalid; + return g->invalid_instruction; } - return const_val->data.x_type; + return result; } 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); + if (pointer_only) + zig_panic("TODO"); + + IrInstruction *result = analyze_const_value(g, context, node, g->builtin_types.entry_type); + if (result->type_entry->id == TypeTableEntryIdInvalid) + return g->builtin_types.entry_invalid; + + assert(result->static_value.ok); + return result->static_value.data.x_type; } // Calls analyze_expression on node, and then resolve_type. @@ -1607,6 +1588,34 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN } } +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 void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { assert(node->type == NodeTypeContainerDecl); @@ -1629,30 +1638,27 @@ static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext } } -static void resolve_struct_instance(CodeGen *g, ImportTableEntry *import, AstNode *node) { - TypeTableEntry *type_entry = node->data.struct_decl.type_entry; - assert(type_entry); +static void count_inline_and_var_args(AstNode *proto_node) { + assert(proto_node->type == NodeTypeFnProto); - // struct/enum member fns will get resolved independently + size_t *inline_arg_count = &proto_node->data.fn_proto.inline_arg_count; + size_t *inline_or_var_type_arg_count = &proto_node->data.fn_proto.inline_or_var_type_arg_count; - 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; - } -} + *inline_arg_count = 0; + *inline_or_var_type_arg_count = 0; -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); + // TODO run these nodes through the type analysis system rather than looking for + // specialized ast nodes. this would get fooled by `{var}` instead of `var` which + // is supposed to be equivalent + for (size_t 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); + if (param_node->data.param_decl.is_inline) { + *inline_arg_count += 1; + *inline_or_var_type_arg_count += 1; + } else if (param_node->data.param_decl.type->type == NodeTypeVarLiteral) { + *inline_or_var_type_arg_count += 1; + } } } @@ -1680,6 +1686,137 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) { node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk; } +static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { + switch (node->type) { + case NodeTypeRoot: + for (size_t 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; + } + count_inline_and_var_args(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 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 NodeTypeZeroesLiteral: + case NodeTypeThisLiteral: + case NodeTypeSymbol: + case NodeTypePrefixOpExpr: + case NodeTypeIfBoolExpr: + case NodeTypeIfVarExpr: + case NodeTypeWhileExpr: + case NodeTypeForExpr: + case NodeTypeSwitchExpr: + case NodeTypeSwitchProng: + case NodeTypeSwitchRange: + case NodeTypeLabel: + case NodeTypeGoto: + case NodeTypeBreak: + case NodeTypeContinue: + case NodeTypeAsmExpr: + case NodeTypeFieldAccessExpr: + case NodeTypeStructField: + case NodeTypeContainerInitExpr: + case NodeTypeStructValueField: + case NodeTypeArrayType: + case NodeTypeErrorType: + case NodeTypeTypeLiteral: + case NodeTypeVarLiteral: + zig_unreachable(); + } +} + +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); + } +} + TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) { TypeTableEntry *underlying_type = get_underlying_type(type_entry); switch (underlying_type->id) { @@ -1718,6 +1855,72 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt zig_unreachable(); } +// Set name to nullptr to make the variable anonymous (not visible to programmer). +// TODO merge with definition of add_local_var in ir.cpp +static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_node, ImportTableEntry *import, + BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node, + bool shadowable) +{ + VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1); + variable_entry->type = type_entry; + variable_entry->block_context = context; + variable_entry->import = import; + variable_entry->shadowable = shadowable; + variable_entry->mem_slot_index = SIZE_MAX; + + 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 && !existing_var->shadowable) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); + add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); + variable_entry->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->src_is_const = is_const; + variable_entry->gen_is_const = is_const; + variable_entry->decl_node = source_node; + variable_entry->val_node = val_node; + + + return variable_entry; +} + +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) +{ + return add_local_var_shadowable(g, source_node, import, context, name, type_entry, is_const, val_node, false); +} + static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeVariableDeclaration); @@ -1739,53 +1942,28 @@ static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) { implicit_type = explicit_type; } else if (var_decl->expr) { - IrExecutable ir_executable = {0}; - IrExecutable analyzed_executable = {0}; - IrInstruction *result = ir_gen(g, var_decl->expr, scope, &ir_executable); - if (result == g->invalid_instruction) { - // ignore the poison value - implicit_type = g->builtin_types.entry_invalid; - } else { - if (g->verbose) { - fprintf(stderr, "var %s = {\n", buf_ptr(var_decl->symbol)); - ir_print(stderr, &ir_executable, 4); - fprintf(stderr, "}\n"); - } - implicit_type = ir_analyze(g, &ir_executable, &analyzed_executable, - explicit_type, var_decl->type); - if (g->verbose) { - fprintf(stderr, "var %s = { // (analyzed)\n", buf_ptr(var_decl->symbol)); - ir_print(stderr, &analyzed_executable, 4); - fprintf(stderr, "}\n"); - } + IrInstruction *result = analyze_const_value(g, scope, var_decl->expr, explicit_type); + assert(result); + implicit_type = result->type_entry; - if (implicit_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, 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, 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, node, buf_sprintf("variable of type 'type' must be constant")); - implicit_type = g->builtin_types.entry_invalid; - } - if (implicit_type->id != TypeTableEntryIdInvalid) { - Expr *expr = get_resolved_expr(var_decl->expr); - IrInstruction *result = ir_exec_const_result(&analyzed_executable); - if (result) { - assert(result->static_value.ok); - expr->const_val = result->static_value; - expr->type_entry = result->type_entry; - } else { - add_node_error(g, first_executing_node(var_decl->expr), - buf_sprintf("global variable initializer requires constant expression")); - implicit_type = g->builtin_types.entry_invalid; - } - } + if (implicit_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, 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, 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, node, buf_sprintf("variable of type 'type' must be constant")); + implicit_type = g->builtin_types.entry_invalid; + } + if (implicit_type->id != TypeTableEntryIdInvalid) { + Expr *expr = get_resolved_expr(var_decl->expr); + assert(result->static_value.ok); + expr->const_val = result->static_value; + expr->type_entry = result->type_entry; } } else if (!is_extern) { add_node_error(g, node, buf_sprintf("variables must be initialized")); @@ -1906,30 +2084,6 @@ void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) { 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: @@ -1968,18 +2122,6 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) { 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); @@ -2105,85 +2247,6 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * return false; } -static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node, - AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count) -{ - TypeTableEntry *prev_type = child_types[0]; - AstNode *prev_node = child_nodes[0]; - if (prev_type->id == TypeTableEntryIdInvalid) { - return prev_type; - } - for (size_t 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) - { - if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { - prev_type = cur_type; - prev_node = cur_node; - } - 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) { @@ -2272,104 +2335,6 @@ static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_ return false; } -static AstNode *create_ast_node(CodeGen *g, ImportTableEntry *import, NodeType kind, AstNode *source_node) { - AstNode *node = allocate<AstNode>(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; -} - -TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import, - BlockContext *block_context, AstNode *parent_source_node, - AstNode **child_nodes, TypeTableEntry **child_types, size_t 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 (size_t 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<BlockContext>(1); context->node = node; @@ -2432,67 +2397,6 @@ TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) { 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 = find_enum_type_field(enum_type, field_name); - if (type_enum_field->type_entry->id == TypeTableEntryIdInvalid) { - return g->builtin_types.entry_invalid; - } - - 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; -} - TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->id == TypeTableEntryIdStruct); assert(type_entry->data.structure.complete); @@ -2587,1601 +2491,6 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { } } -static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g, - TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type) -{ - assert(node->type == NodeTypeFieldAccessExpr); - if (!is_slice(bare_struct_type)) { - BlockContext *container_block_context = get_container_block_context(bare_struct_type); - assert(container_block_context); - 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); - } - } - } - add_node_error(g, node, - buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); - return g->builtin_types.entry_invalid; -} - -static TypeTableEntry *analyze_container_member_access(CodeGen *g, - Buf *field_name, AstNode *node, TypeTableEntry *struct_type) -{ - TypeTableEntry *bare_type = container_ref_type(struct_type); - if (!type_is_complete(bare_type)) { - resolve_container_type(g, bare_type); - } - - node->data.field_access_expr.bare_container_type = bare_type; - - if (bare_type->id == TypeTableEntryIdStruct) { - node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_type, field_name); - if (node->data.field_access_expr.type_struct_field) { - return node->data.field_access_expr.type_struct_field->type_entry; - } else { - return analyze_container_member_access_inner(g, bare_type, field_name, - node, struct_type); - } - } else if (bare_type->id == TypeTableEntryIdEnum) { - node->data.field_access_expr.type_enum_field = find_enum_type_field(bare_type, field_name); - if (node->data.field_access_expr.type_enum_field) { - return node->data.field_access_expr.type_enum_field->type_entry; - } else { - return analyze_container_member_access_inner(g, bare_type, field_name, - node, struct_type); - } - } else if (bare_type->id == TypeTableEntryIdUnion) { - zig_panic("TODO"); - } else { - zig_unreachable(); - } -} - -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; - - if (struct_type->id == TypeTableEntryIdInvalid) { - return struct_type; - } else if (is_container_ref(struct_type)) { - return analyze_container_member_access(g, field_name, node, struct_type); - } 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); - size_t 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 { - 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 { - 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 (size_t 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 && decl_node->owner != import) { - 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, LValPurpose purpose) -{ - 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) { - if (array_type->data.pointer.is_const && purpose == LValPurposeAssign) { - add_node_error(g, node, buf_sprintf("cannot assign to constant")); - return g->builtin_types.entry_invalid; - } - return_type = array_type->data.pointer.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; - if (pointer_type->data.pointer.is_const && purpose == LValPurposeAssign) { - add_node_error(g, node, buf_sprintf("cannot assign to constant")); - return g->builtin_types.entry_invalid; - } - return_type = pointer_type->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_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_c_string_lit(CodeGen *g, AstNode *node, Buf *str) { - Expr *expr = get_resolved_expr(node); - expr->const_val.ok = true; - - size_t len_with_null = buf_len(str) + 1; - expr->const_val.data.x_ptr.ptr = allocate<ConstExprValue*>(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<ConstExprValue>(len_with_null); - for (size_t 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<ConstExprValue*>(buf_len(str)); - - ConstExprValue *all_chars = allocate<ConstExprValue>(buf_len(str)); - for (size_t 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_bignum(CodeGen *g, AstNode *node, - TypeTableEntry *expected_type, BigNum *bignum, 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_bignum(&expr->const_val.data.x_bignum, bignum); - if (bignum->kind == BigNumKindInt) { - return g->builtin_types.entry_num_lit_int; - } else if (bignum->kind == BigNumKindFloat) { - return g->builtin_types.entry_num_lit_float; - } else { - zig_unreachable(); - } -} - -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 *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->src_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(g, context, source_node); - } - if (var->src_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) -{ - 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(g, context, node); - 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 BinOpTypeAssignTimesWrap: - case BinOpTypeAssignDiv: - case BinOpTypeAssignMod: - return type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdFloat; - case BinOpTypeAssignPlus: - case BinOpTypeAssignPlusWrap: - case BinOpTypeAssignMinus: - case BinOpTypeAssignMinusWrap: - return type->id == TypeTableEntryIdInt || - type->id == TypeTableEntryIdFloat || - type->id == TypeTableEntryIdPointer; - case BinOpTypeAssignBitShiftLeft: - case BinOpTypeAssignBitShiftLeftWrap: - 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 BinOpTypeBitShiftLeftWrap: - case BinOpTypeBitShiftRight: - case BinOpTypeAdd: - case BinOpTypeAddWrap: - case BinOpTypeSub: - case BinOpTypeSubWrap: - case BinOpTypeMult: - case BinOpTypeMultWrap: - case BinOpTypeDiv: - case BinOpTypeMod: - case BinOpTypeUnwrapMaybe: - case BinOpTypeArrayCat: - case BinOpTypeArrayMult: - zig_unreachable(); - } - zig_unreachable(); -} - -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->src_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, purpose); - } 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 TypeTableEntryIdBlock: - 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 TypeTableEntryIdNullLit: - 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; - - case TypeTableEntryIdVar: - zig_unreachable(); - } - - 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<ConstExprValue*>(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 BinOpTypeAssignTimesWrap: - case BinOpTypeAssignDiv: - case BinOpTypeAssignMod: - case BinOpTypeAssignPlus: - case BinOpTypeAssignPlusWrap: - case BinOpTypeAssignMinus: - case BinOpTypeAssignMinusWrap: - case BinOpTypeAssignBitShiftLeft: - case BinOpTypeAssignBitShiftLeftWrap: - 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 BinOpTypeBitShiftLeftWrap: - case BinOpTypeBitShiftRight: - case BinOpTypeAdd: - case BinOpTypeAddWrap: - case BinOpTypeSub: - case BinOpTypeSubWrap: - case BinOpTypeMult: - case BinOpTypeMultWrap: - 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<ConstExprValue*>(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<ConstExprValue*>(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). -// TODO merge with definition of add_local_var in ir.cpp -static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_node, ImportTableEntry *import, - BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node, - bool shadowable) -{ - VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1); - variable_entry->type = type_entry; - variable_entry->block_context = context; - variable_entry->import = import; - variable_entry->shadowable = shadowable; - variable_entry->mem_slot_index = SIZE_MAX; - - 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 && !existing_var->shadowable) { - ErrorMsg *msg = add_node_error(g, source_node, - buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); - add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->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->src_is_const = is_const; - variable_entry->gen_is_const = is_const; - variable_entry->decl_node = source_node; - variable_entry->val_node = val_node; - - - return variable_entry; -} - -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) -{ - return add_local_var_shadowable(g, source_node, import, context, name, type_entry, is_const, val_node, false); -} - -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); - - ConstExprValue *const_val = &get_resolved_expr(node)->const_val; - const_val->ok = true; - - return g->builtin_types.entry_null; -} - -static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node) -{ - assert(node->type == NodeTypeUndefinedLiteral); - - Expr *expr = get_resolved_expr(node); - ConstExprValue *const_val = &expr->const_val; - - const_val->ok = true; - const_val->special = ConstValSpecialUndef; - - return expected_type ? expected_type : g->builtin_types.entry_undef; -} - -static TypeTableEntry *analyze_zeroes_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->special = ConstValSpecialZeroes; - - 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) -{ - return resolve_expr_const_val_as_bignum(g, node, expected_type, node->data.number_literal.bignum, false); -} - -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_pointer_only(g, import, context, - node->data.array_type.child_type, true); - - 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) { - child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type); - 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 { - TypeTableEntry *slice_type = get_slice_type(g, child_type, node->data.array_type.is_const); - return resolve_expr_const_val_as_type(g, node, slice_type, 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, nullptr); - - 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_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->special == ConstValSpecialUndef) { - 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); -} - bool type_is_codegen_pointer(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return true; if (type->id == TypeTableEntryIdFn) return true; @@ -4192,1066 +2501,7 @@ bool type_is_codegen_pointer(TypeTableEntry *type) { return false; } -static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type, - TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry) -{ - ErrorMsg *msg = add_node_error(g, node, - buf_sprintf("function called as method of '%s', but first parameter is of type '%s'", - buf_ptr(&container_type->name), - buf_ptr(&expected_param_type->name))); - if (fn_table_entry) { - add_error_note(g, msg, fn_table_entry->proto_node, buf_sprintf("function declared here")); - } - return g->builtin_types.entry_invalid; -} - -// 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 - size_t struct_node_1_or_0 = struct_node ? 1 : 0; - size_t 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); - size_t call_param_count = node->data.fn_call_expr.params.length; - size_t expect_arg_count = src_param_count - struct_node_1_or_0; - - bool ok_invocation = true; - - if (fn_type->data.fn.fn_type_id.is_var_args) { - if (call_param_count < expect_arg_count) { - ok_invocation = false; - add_node_error(g, node, - buf_sprintf("expected at least %zu arguments, got %zu", src_param_count, call_param_count)); - } - } else if (expect_arg_count != call_param_count) { - ok_invocation = false; - add_node_error(g, node, - buf_sprintf("expected %zu arguments, got %zu", expect_arg_count, call_param_count)); - } - - bool all_args_const_expr = true; - - if (struct_node) { - Expr *struct_expr = get_resolved_expr(struct_node); - ConstExprValue *struct_const_val = &struct_expr->const_val; - if (!struct_const_val->ok) { - all_args_const_expr = false; - } - - FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[0]; - TypeTableEntry *expected_param_type = param_info->type; - TypeTableEntry *container_bare_type = container_ref_type(struct_expr->type_entry); - if (is_container_ref(expected_param_type)) { - TypeTableEntry *param_bare_type = container_ref_type(expected_param_type); - if (param_bare_type != container_bare_type) { - return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry); - } - } else { - return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry); - } - } - - // 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. - size_t next_type_i = struct_node_1_or_0; - for (size_t call_i = 0; call_i < call_param_count; call_i += 1) { - size_t 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 (fn_table_entry->proto_node->data.fn_proto.skip) { - return g->builtin_types.entry_invalid; - } - } - 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(g, context, node); - if (fn_table_entry && fn_table_entry->want_pure == WantPureTrue) { - return g->builtin_types.entry_invalid; - } - } - - // TODO - //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 - size_t struct_node_1_or_0 = (struct_node ? 1 : 0); - size_t src_param_count = decl_node->data.fn_proto.params.length; - size_t 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 %zu arguments, got %zu", src_param_count - struct_node_1_or_0, call_param_count)); - return g->builtin_types.entry_invalid; - } - - size_t inline_or_var_type_arg_count = decl_node->data.fn_proto.inline_or_var_type_arg_count; - assert(inline_or_var_type_arg_count > 0); - - BlockContext *child_context = decl_node->owner->block_context; - size_t next_generic_param_index = 0; - - GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1); - generic_fn_type_id->decl_node = decl_node; - generic_fn_type_id->generic_param_count = inline_or_var_type_arg_count; - generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_or_var_type_arg_count); - - size_t next_impl_i = 0; - for (size_t call_i = 0; call_i < call_param_count; call_i += 1) { - size_t 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); - - 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; - } - - bool is_var_type = (expected_param_type->id == TypeTableEntryIdVar); - bool is_inline = generic_param_decl_node->data.param_decl.is_inline; - if (!is_inline && !is_var_type) { - next_impl_i += 1; - continue; - } - - - AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i); - TypeTableEntry *param_type = analyze_expression(g, import, parent_context, - is_var_type ? nullptr : 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 (is_inline && !const_val->ok) { - add_node_error(g, *param_node, - buf_sprintf("unable to evaluate constant expression for inline parameter")); - - return g->builtin_types.entry_invalid; - } - - VariableTableEntry *var = add_local_var_shadowable(g, generic_param_decl_node, decl_node->owner, child_context, - generic_param_decl_node->data.param_decl.name, param_type, true, *param_node, true); - // 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; - - GenericParamValue *generic_param_value = - &generic_fn_type_id->generic_params[next_generic_param_index]; - generic_param_value->type = param_type; - generic_param_value->node = is_inline ? *param_node : nullptr; - generic_param_value->impl_index = next_impl_i; - next_generic_param_index += 1; - - if (!is_inline) { - next_impl_i += 1; - } - } - - assert(next_generic_param_index == inline_or_var_type_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.inline_or_var_type_arg_count = 0; - impl_decl_node->data.fn_proto.generic_proto_node = decl_node; - - // replace var arg types with actual types - for (size_t generic_arg_i = 0; generic_arg_i < inline_or_var_type_arg_count; generic_arg_i += 1) { - GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[generic_arg_i]; - if (!generic_param_value->node) { - size_t impl_i = generic_param_value->impl_index; - AstNode *impl_param_decl_node = impl_decl_node->data.fn_proto.params.at(impl_i); - assert(impl_param_decl_node->type == NodeTypeParamDecl); - - impl_param_decl_node->data.param_decl.type = create_ast_type_node(g, import, - generic_param_value->type, impl_param_decl_node); - normalize_parent_ptrs(impl_param_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<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params; - - size_t expected_param_count = generic_params->length; - size_t 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 %zu arguments, got %zu", expected_param_count, actual_param_count)); - return g->builtin_types.entry_invalid; - } - - GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(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<GenericParamValue>(actual_param_count); - - BlockContext *child_context = decl_node->owner->block_context; - for (size_t 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) { - zig_panic("moved builtin fn call code to ir.cpp"); - } - - 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) { - zig_unreachable(); - } 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_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; - - - size_t prong_count = node->data.switch_expr.prongs.length; - AstNode **peer_nodes = allocate<AstNode*>(prong_count); - TypeTableEntry **peer_types = allocate<TypeTableEntry*>(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; - } - - - size_t *field_use_counts = nullptr; - HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes = {}; - if (expr_type->id == TypeTableEntryIdEnum) { - field_use_counts = allocate<size_t>(expr_type->data.enumeration.src_field_count); - } else if (expr_type->id == TypeTableEntryIdErrorUnion) { - err_use_nodes.init(10); - } - - size_t *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index; - *const_chosen_prong_index = SIZE_MAX; - AstNode *else_prong = nullptr; - for (size_t 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 == SIZE_MAX && expr_val->ok) { - *const_chosen_prong_index = prong_i; - } - } else { - bool all_agree_on_var_type = true; - var_type = nullptr; - - for (size_t 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 = find_enum_type_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 (size_t 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); - - 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); - } - // This must go after the analyze_expression for - // prong_node->data.switch_prong.expr because of AST rewriting. - peer_nodes[prong_i] = prong_node->data.switch_prong.expr; - } - - if (expr_type->id == TypeTableEntryIdEnum && !else_prong) { - for (uint32_t i = 0; i < expr_type->data.enumeration.src_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 != SIZE_MAX); - - *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 (!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: - zig_panic("TODO moved to ir.cpp"); - 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) { - if (expected_return_type->id != TypeTableEntryIdErrorUnion && - expected_return_type->id != TypeTableEntryIdPureError) - { - ErrorMsg *msg = add_node_error(g, node, - buf_sprintf("%%return statement in function with return type '%s'", - buf_ptr(&expected_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: - { - TypeTableEntry *expected_maybe_type; - if (expected_type) { - expected_maybe_type = get_maybe_type(g, expected_type); - } else { - expected_maybe_type = nullptr; - } - TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type, - node->data.return_expr.expr); - if (resolved_type->id == TypeTableEntryIdInvalid) { - return resolved_type; - } else if (resolved_type->id == TypeTableEntryIdMaybe) { - if (expected_return_type->id != TypeTableEntryIdMaybe) { - ErrorMsg *msg = add_node_error(g, node, - buf_sprintf("?return statement in function with return type '%s'", - buf_ptr(&expected_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.maybe.child_type; - } else { - add_node_error(g, node->data.return_expr.expr, - buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name))); - return g->builtin_types.entry_invalid; - } - } - } - 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 (size_t 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<LabelTableEntry>(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(g, context, node); - - node->data.asm_expr.return_count = 0; - TypeTableEntry *return_type = g->builtin_types.entry_void; - for (size_t 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 (size_t 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 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, LValPurposeAddressOf); - 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: - zig_panic("analyze container init moved to ir.cpp"); - 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: - zig_panic("moved to ir.cpp"); - 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 NodeTypeZeroesLiteral: - return_type = analyze_zeroes_literal_expr(g, import, context, expected_type, node); - break; - case NodeTypeThisLiteral: - zig_panic("moved to ir.cpp"); - break; - case NodeTypeSymbol: - return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only); - break; - case NodeTypePrefixOpExpr: - zig_panic("moved to ir.cpp"); - 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: - zig_panic("moved to ir.cpp"); - 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 NodeTypeVarLiteral: - return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_var, false); - break; - case NodeTypeSwitchProng: - case NodeTypeSwitchRange: - 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; @@ -5272,6 +2522,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { BlockContext *context = node->data.fn_def.block_context; TypeTableEntry *fn_type = fn_table_entry->type_entry; + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; for (size_t i = 0; i < fn_proto->params.length; i += 1) { AstNode *param_decl_node = fn_proto->params.at(i); @@ -5279,7 +2530,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { // define local variables for parameters AstNodeParamDecl *param_decl = ¶m_decl_node->data.param_decl; - TypeTableEntry *type = unwrapped_node_type(param_decl->type); + TypeTableEntry *type = fn_type_id->param_info[i].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")); @@ -5340,162 +2591,6 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { 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 void count_inline_and_var_args(AstNode *proto_node) { - assert(proto_node->type == NodeTypeFnProto); - - size_t *inline_arg_count = &proto_node->data.fn_proto.inline_arg_count; - size_t *inline_or_var_type_arg_count = &proto_node->data.fn_proto.inline_or_var_type_arg_count; - - *inline_arg_count = 0; - *inline_or_var_type_arg_count = 0; - - // TODO run these nodes through the type analysis system rather than looking for - // specialized ast nodes. this would get fooled by `{var}` instead of `var` which - // is supposed to be equivalent - for (size_t 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); - if (param_node->data.param_decl.is_inline) { - *inline_arg_count += 1; - *inline_or_var_type_arg_count += 1; - } else if (param_node->data.param_decl.type->type == NodeTypeVarLiteral) { - *inline_or_var_type_arg_count += 1; - } - } -} - -static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { - switch (node->type) { - case NodeTypeRoot: - for (size_t 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; - } - count_inline_and_var_args(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 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 NodeTypeZeroesLiteral: - case NodeTypeThisLiteral: - case NodeTypeSymbol: - case NodeTypePrefixOpExpr: - case NodeTypeIfBoolExpr: - case NodeTypeIfVarExpr: - case NodeTypeWhileExpr: - case NodeTypeForExpr: - case NodeTypeSwitchExpr: - case NodeTypeSwitchProng: - case NodeTypeSwitchRange: - case NodeTypeLabel: - case NodeTypeGoto: - case NodeTypeBreak: - case NodeTypeContinue: - case NodeTypeAsmExpr: - case NodeTypeFieldAccessExpr: - case NodeTypeStructField: - case NodeTypeContainerInitExpr: - case NodeTypeStructValueField: - case NodeTypeArrayType: - case NodeTypeErrorType: - case NodeTypeTypeLiteral: - case NodeTypeVarLiteral: - 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; @@ -5565,11 +2660,11 @@ static void resolve_use_decl(CodeGen *g, AstNode *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) { + + IrInstruction *result = analyze_const_value(g, tld->import->block_context, node->data.use.expr, + g->builtin_types.entry_namespace); + if (result->type_entry->id == TypeTableEntryIdInvalid) tld->import->any_imports_failed = true; - } } ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, diff --git a/src/analyze.hpp b/src/analyze.hpp index 605fe0ae6d..b6bcf5868c 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -45,10 +45,6 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, AstNode *first_executing_node(AstNode *node); -TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import, - BlockContext *block_context, AstNode *parent_source_node, - AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count); - // TODO move these over, these used to be static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 1a8463e85d..c7459f8ef2 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -261,17 +261,6 @@ static void print_indent(AstRender *ar) { } } -static bool is_node_void(AstNode *node) { - if (node->type == NodeTypeSymbol) { - if (node->data.symbol_expr.override_type_entry) { - return node->data.symbol_expr.override_type_entry->id == TypeTableEntryIdVoid; - } else if (buf_eql_str(node->data.symbol_expr.symbol, "void")) { - return true; - } - } - return false; -} - static bool is_alpha_under(uint8_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; @@ -406,10 +395,8 @@ static void render_node(AstRender *ar, AstNode *node) { fprintf(ar->f, ")"); AstNode *return_type_node = node->data.fn_proto.return_type; - if (!is_node_void(return_type_node)) { - fprintf(ar->f, " -> "); - render_node(ar, return_type_node); - } + fprintf(ar->f, " -> "); + render_node(ar, return_type_node); break; } case NodeTypeFnDef: @@ -521,14 +508,7 @@ static void render_node(AstRender *ar, AstNode *node) { break; } case NodeTypeSymbol: - { - TypeTableEntry *override_type = node->data.symbol_expr.override_type_entry; - if (override_type) { - fprintf(ar->f, "%s", buf_ptr(&override_type->name)); - } else { - print_symbol(ar, node->data.symbol_expr.symbol); - } - } + print_symbol(ar, node->data.symbol_expr.symbol); break; case NodeTypePrefixOpExpr: { @@ -623,10 +603,8 @@ static void render_node(AstRender *ar, AstNode *node) { assert(field_node->type == NodeTypeStructField); print_indent(ar); print_symbol(ar, field_node->data.struct_field.name); - if (!is_node_void(field_node->data.struct_field.type)) { - fprintf(ar->f, ": "); - render_node(ar, field_node->data.struct_field.type); - } + fprintf(ar->f, ": "); + render_node(ar, field_node->data.struct_field.type); fprintf(ar->f, ",\n"); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f19279bfc..086394a950 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2965,6 +2965,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdToPtrType: case IrInstructionIdPtrTypeChild: case IrInstructionIdFieldPtr: + case IrInstructionIdSetFnTest: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -2996,7 +2997,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction); case IrInstructionIdSwitchBr: case IrInstructionIdPhi: - case IrInstructionIdBuiltinCall: case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: case IrInstructionIdReadField: @@ -5303,8 +5303,6 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) { } static void get_c_type_node(CodeGen *g, AstNode *type_node, Buf *out_buf) { - assert(type_node->type != NodeTypeSymbol || !type_node->data.symbol_expr.override_type_entry); - Expr *expr = get_resolved_expr(type_node); assert(expr->type_entry); assert(expr->type_entry->id == TypeTableEntryIdMetaType); diff --git a/src/ir.cpp b/src/ir.cpp index 52a016185d..96bf78bdf5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -149,10 +149,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCall *) { return IrInstructionIdCall; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionBuiltinCall *) { - return IrInstructionIdBuiltinCall; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) { return IrInstructionIdConst; } @@ -189,6 +185,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *) return IrInstructionIdPtrTypeChild; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) { + return IrInstructionIdSetFnTest; +} + template<typename T> static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { T *special_instruction = allocate<T>(1); @@ -353,7 +353,7 @@ static IrInstruction *ir_build_const_scope(IrBuilder *irb, AstNode *source_node, static IrInstruction *ir_build_const_bool(IrBuilder *irb, AstNode *source_node, bool value) { IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node); - const_instruction->base.type_entry = irb->codegen->builtin_types.entry_block; + const_instruction->base.type_entry = irb->codegen->builtin_types.entry_bool; const_instruction->base.static_value.ok = true; const_instruction->base.static_value.data.x_bool = value; return &const_instruction->base; @@ -505,20 +505,6 @@ static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_inst return new_instruction; } -static IrInstruction *ir_build_builtin_call(IrBuilder *irb, AstNode *source_node, - BuiltinFnEntry *fn, IrInstruction **args) -{ - IrInstructionBuiltinCall *call_instruction = ir_build_instruction<IrInstructionBuiltinCall>(irb, source_node); - call_instruction->fn = fn; - call_instruction->args = args; - - for (size_t i = 0; i < fn->param_count; i += 1) { - ir_ref_instruction(args[i]); - } - - return &call_instruction->base; -} - static IrInstruction *ir_build_phi(IrBuilder *irb, AstNode *source_node, size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values) { @@ -717,6 +703,19 @@ static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, AstNode *source_no return &instruction->base; } +static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, AstNode *source_node, IrInstruction *fn_value, + IrInstruction *is_test) +{ + IrInstructionSetFnTest *instruction = ir_build_instruction<IrInstructionSetFnTest>(irb, source_node); + instruction->fn_value = fn_value; + instruction->is_test = is_test; + + ir_ref_instruction(fn_value); + ir_ref_instruction(is_test); + + return &instruction->base; +} + //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { // size_t result = 0; // while (inner_block != outer_block) { @@ -1066,10 +1065,6 @@ static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstN static IrInstruction *ir_gen_symbol(IrBuilder *irb, AstNode *node, LValPurpose lval) { assert(node->type == NodeTypeSymbol); - if (node->data.symbol_expr.override_type_entry) { - zig_panic("TODO have parseh directly generate IR"); - } - Buf *variable_name = node->data.symbol_expr.symbol; auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name); @@ -1163,26 +1158,71 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) { builtin_fn->ref_count += 1; - if (builtin_fn->id == BuiltinFnIdUnreachable) { - return ir_build_unreachable(irb, node); - } else if (builtin_fn->id == BuiltinFnIdTypeof) { - AstNode *arg_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg = ir_gen_node(irb, arg_node, node->block_context); - if (arg == irb->codegen->invalid_instruction) - return arg; - return ir_build_typeof(irb, node, arg); - } + switch (builtin_fn->id) { + case BuiltinFnIdInvalid: + zig_unreachable(); + case BuiltinFnIdUnreachable: + return ir_build_unreachable(irb, node); + case BuiltinFnIdTypeof: + { + AstNode *arg_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg = ir_gen_node(irb, arg_node, node->block_context); + if (arg == irb->codegen->invalid_instruction) + return arg; + return ir_build_typeof(irb, node, arg); + } + case BuiltinFnIdSetFnTest: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; - IrInstruction **args = allocate<IrInstruction *>(actual_param_count); - for (size_t i = 0; i < actual_param_count; i += 1) { - AstNode *arg_node = node->data.fn_call_expr.params.at(i); - IrInstruction *arg = ir_gen_node(irb, arg_node, node->block_context); - if (arg == irb->codegen->invalid_instruction) - return arg; - args[i] = arg; - } + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, node->block_context); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; - return ir_build_builtin_call(irb, node, builtin_fn, args); + return ir_build_set_fn_test(irb, node, arg0_value, arg1_value); + } + case BuiltinFnIdMemcpy: + case BuiltinFnIdMemset: + case BuiltinFnIdSizeof: + case BuiltinFnIdAlignof: + case BuiltinFnIdMaxValue: + case BuiltinFnIdMinValue: + case BuiltinFnIdMemberCount: + case BuiltinFnIdAddWithOverflow: + case BuiltinFnIdSubWithOverflow: + case BuiltinFnIdMulWithOverflow: + case BuiltinFnIdShlWithOverflow: + case BuiltinFnIdCInclude: + case BuiltinFnIdCDefine: + case BuiltinFnIdCUndef: + case BuiltinFnIdCompileVar: + case BuiltinFnIdCompileErr: + case BuiltinFnIdConstEval: + case BuiltinFnIdCtz: + case BuiltinFnIdClz: + case BuiltinFnIdImport: + case BuiltinFnIdCImport: + case BuiltinFnIdErrName: + case BuiltinFnIdBreakpoint: + case BuiltinFnIdReturnAddress: + case BuiltinFnIdFrameAddress: + case BuiltinFnIdEmbedFile: + case BuiltinFnIdCmpExchange: + case BuiltinFnIdFence: + case BuiltinFnIdDivExact: + case BuiltinFnIdTruncate: + case BuiltinFnIdIntType: + case BuiltinFnIdSetFnVisible: + case BuiltinFnIdSetFnStaticEval: + case BuiltinFnIdSetFnNoInline: + case BuiltinFnIdSetDebugSafety: + zig_panic("TODO IR gen more builtin functions"); + } + zig_unreachable(); } static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) { @@ -2044,6 +2084,53 @@ static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value return const_val->data.x_type; } +static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out) { + if (bool_value == ira->codegen->invalid_instruction) + return false; + + if (bool_value->type_entry->id == TypeTableEntryIdInvalid) + return false; + + if (bool_value->type_entry->id != TypeTableEntryIdBool) { + add_node_error(ira->codegen, bool_value->source_node, + buf_sprintf("expected type 'bool', found '%s'", buf_ptr(&bool_value->type_entry->name))); + return false; + } + + ConstExprValue *const_val = &bool_value->static_value; + if (!const_val->ok) { + add_node_error(ira->codegen, bool_value->source_node, + buf_sprintf("unable to evaluate constant expression")); + return false; + } + + *out = const_val->data.x_bool; + return true; +} + +static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { + if (fn_value == ira->codegen->invalid_instruction) + return nullptr; + + if (fn_value->type_entry->id == TypeTableEntryIdInvalid) + return nullptr; + + if (fn_value->type_entry->id != TypeTableEntryIdFn) { + add_node_error(ira->codegen, fn_value->source_node, + buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->type_entry->name))); + return nullptr; + } + + ConstExprValue *const_val = &fn_value->static_value; + if (!const_val->ok) { + add_node_error(ira->codegen, fn_value->source_node, + buf_sprintf("unable to evaluate constant expression")); + return nullptr; + } + + return const_val->data.x_fn; +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *dest_type, IrInstruction *value) { @@ -2837,11 +2924,18 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction } else if (fn_ref->type_entry->id == TypeTableEntryIdFn) { // TODO fully port over the fn call analyze code to IR FnTableEntry *fn_table_entry = fn_ref->static_value.data.x_fn; + TypeTableEntry *fn_type = fn_table_entry->type_entry; + + IrInstruction **casted_args = allocate<IrInstruction *>(call_instruction->arg_count); + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + TypeTableEntry *param_type = fn_type->data.fn.fn_type_id.param_info[i].type; + IrInstruction *old_arg = call_instruction->args[i]->other; + casted_args[i] = ir_get_casted_value(ira, old_arg, param_type); + } ir_build_call_from(&ira->new_irb, &call_instruction->base, - call_instruction->fn, call_instruction->arg_count, call_instruction->args); + call_instruction->fn, call_instruction->arg_count, casted_args); - TypeTableEntry *fn_type = fn_table_entry->type_entry; TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; return return_type; } else { @@ -3075,6 +3169,696 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio zig_unreachable(); } +static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) { + IrBasicBlock *old_dest_block = br_instruction->dest_block; + + // TODO detect backward jumps + + if (br_instruction->is_inline || old_dest_block->ref_count == 1) { + ir_inline_bb(ira, old_dest_block); + return ira->codegen->builtin_types.entry_unreachable; + } + + IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block); + ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb); + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; +} + +static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { + TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; + IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type); + if (condition == ira->codegen->invalid_instruction) { + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; + } + + // TODO detect backward jumps + if (condition->static_value.ok) { + IrBasicBlock *old_dest_block = condition->static_value.data.x_bool ? + cond_br_instruction->then_block : cond_br_instruction->else_block; + + if (cond_br_instruction->is_inline || old_dest_block->ref_count == 1) { + ir_inline_bb(ira, old_dest_block); + return ira->codegen->builtin_types.entry_unreachable; + } + } else if (cond_br_instruction->is_inline) { + add_node_error(ira->codegen, condition->source_node, + buf_sprintf("unable to evaluate constant expression")); + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; + } + + IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block); + IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block); + ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, condition, new_then_block, new_else_block, false); + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; +} + +static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira, + IrInstructionUnreachable *unreachable_instruction) +{ + ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base); + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; +} + +static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) { + if (ira->const_predecessor_bb) { + for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { + IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; + if (predecessor != ira->const_predecessor_bb) + continue; + IrInstruction *value = phi_instruction->incoming_values[i]->other; + assert(value->type_entry); + if (value->static_value.ok) { + ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base, + value->static_value.depends_on_compile_var); + *out_val = value->static_value; + } else { + phi_instruction->base.other = value; + } + return value->type_entry; + } + zig_unreachable(); + } + + ZigList<IrBasicBlock*> new_incoming_blocks = {0}; + ZigList<IrInstruction*> new_incoming_values = {0}; + + for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { + IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; + if (predecessor->ref_count == 0) + continue; + + assert(predecessor->other); + new_incoming_blocks.append(predecessor->other); + + IrInstruction *old_value = phi_instruction->incoming_values[i]; + assert(old_value); + new_incoming_values.append(old_value->other); + } + assert(new_incoming_blocks.length != 0); + + if (new_incoming_blocks.length == 1) { + IrInstruction *first_value = new_incoming_values.at(0); + phi_instruction->base.other = first_value; + return first_value->type_entry; + } + + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, + new_incoming_values.items, new_incoming_values.length); + if (resolved_type->id == TypeTableEntryIdInvalid) + return resolved_type; + + ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length, + new_incoming_blocks.items, new_incoming_values.items); + return resolved_type; +} + +static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) { + VariableTableEntry *var = var_ptr_instruction->var; + if (var->type->id == TypeTableEntryIdInvalid) + return var->type; + + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var->type, false); + // TODO once the anlayze code is fully ported over to IR we won't need this SIZE_MAX thing. + if (var->mem_slot_index != SIZE_MAX) { + ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; + if (mem_slot->ok) { + ConstExprValue *out_val = ir_build_const_from(ira, &var_ptr_instruction->base, + mem_slot->depends_on_compile_var); + + out_val->data.x_ptr.len = 1; + out_val->data.x_ptr.is_c_str = false; + out_val->data.x_ptr.ptr = allocate<ConstExprValue *>(1); + out_val->data.x_ptr.ptr[0] = mem_slot; + return ptr_type; + } + } + + ir_build_var_ptr_from(&ira->new_irb, &var_ptr_instruction->base, var); + return ptr_type; +} + +static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { + IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other; + IrInstruction *elem_index = elem_ptr_instruction->elem_index->other; + + TypeTableEntry *array_type = array_ptr->type_entry; + TypeTableEntry *return_type; + + if (array_type->id == TypeTableEntryIdInvalid) { + return array_type; + } else if (array_type->id == TypeTableEntryIdArray) { + if (array_type->data.array.len == 0) { + add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, + buf_sprintf("out of bounds array access")); + } + TypeTableEntry *child_type = array_type->data.array.child_type; + return_type = get_pointer_to_type(ira->codegen, child_type, false); + } else if (array_type->id == TypeTableEntryIdPointer) { + return_type = array_type; + } else if (is_slice(array_type)) { + return_type = array_type->data.structure.fields[0].type_entry; + } else { + add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, + buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; + IrInstruction *casted_elem_index = ir_get_casted_value(ira, elem_index, usize); + if (casted_elem_index == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + if (array_ptr->static_value.ok && casted_elem_index->static_value.ok) { + zig_panic("TODO compile time array access"); + } + + ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, casted_elem_index); + + return return_type; +} + +static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira, + TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction, + TypeTableEntry *container_type) +{ + if (!is_slice(bare_struct_type)) { + BlockContext *container_block_context = get_container_block_context(bare_struct_type); + assert(container_block_context); + 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) { + zig_panic("TODO member function call"); + } + } + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); + return ira->codegen->builtin_types.entry_invalid; +} + + +static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *field_name, + IrInstructionFieldPtr *field_ptr_instruction, TypeTableEntry *container_type) +{ + IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; + TypeTableEntry *bare_type = container_ref_type(container_type); + if (!type_is_complete(bare_type)) { + resolve_container_type(ira->codegen, bare_type); + } + + if (bare_type->id == TypeTableEntryIdStruct) { + TypeStructField *field = find_struct_type_field(bare_type, field_name); + if (field) { + ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); + return get_pointer_to_type(ira->codegen, field->type_entry, false); + } else { + return ir_analyze_container_member_access_inner(ira, bare_type, field_name, + field_ptr_instruction, container_type); + } + } else if (bare_type->id == TypeTableEntryIdEnum) { + zig_panic("TODO enum field ptr"); + } else if (bare_type->id == TypeTableEntryIdUnion) { + zig_panic("TODO"); + } else { + zig_unreachable(); + } +} + +static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { + IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; + Buf *field_name = field_ptr_instruction->field_name; + + TypeTableEntry *container_type = container_ptr->type_entry; + if (container_type->id == TypeTableEntryIdInvalid) { + return container_type; + } else if (is_container_ref(container_type)) { + return ir_analyze_container_member_access(ira, field_name, field_ptr_instruction, container_type); + } else if (container_type->id == TypeTableEntryIdArray) { + if (buf_eql_str(field_name, "len")) { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("pointer to array length not available")); + return ira->codegen->builtin_types.entry_invalid; + } else { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), + buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdMetaType) { + TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr); + + if (child_type->id == TypeTableEntryIdInvalid) { + return ira->codegen->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdEnum) { + zig_panic("TODO enum type field"); + } else if (child_type->id == TypeTableEntryIdStruct) { + zig_panic("TODO struct type field"); + } else if (child_type->id == TypeTableEntryIdPureError) { + zig_panic("TODO error type field"); + } else if (child_type->id == TypeTableEntryIdInt) { + zig_panic("TODO integer type field"); + } else { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdNamespace) { + zig_panic("TODO namespace field access"); + } else { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + +static TypeTableEntry *ir_analyze_read_field_as_ptr_load(IrAnalyze *ira, + IrInstructionReadField *read_field_instruction) +{ + IrInstruction *old_field_ptr_inst = ir_build_field_ptr(&ira->old_irb, read_field_instruction->base.source_node, + read_field_instruction->container_ptr, read_field_instruction->field_name); + IrInstruction *old_load_ptr_inst = ir_build_load_ptr(&ira->old_irb, read_field_instruction->base.source_node, + old_field_ptr_inst); + ir_analyze_instruction(ira, old_field_ptr_inst); + TypeTableEntry *result_type = ir_analyze_instruction(ira, old_load_ptr_inst); + read_field_instruction->base.other = old_load_ptr_inst->other; + return result_type; +} + +static TypeTableEntry *ir_analyze_instruction_read_field(IrAnalyze *ira, + IrInstructionReadField *read_field_instruction) +{ + IrInstruction *container_ptr = read_field_instruction->container_ptr->other; + Buf *field_name = read_field_instruction->field_name; + + TypeTableEntry *container_type = container_ptr->type_entry; + if (container_type->id == TypeTableEntryIdInvalid) { + return container_type; + } else if (is_container_ref(container_type)) { + return ir_analyze_read_field_as_ptr_load(ira, read_field_instruction); + } else if (container_type->id == TypeTableEntryIdArray) { + if (buf_eql_str(field_name, "len")) { + return ir_analyze_const_usize(ira, &read_field_instruction->base, container_type->data.array.len, false); + } else { + add_node_error(ira->codegen, read_field_instruction->base.source_node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), + buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdMetaType) { + TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr); + + if (child_type->id == TypeTableEntryIdInvalid) { + return ira->codegen->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdEnum) { + zig_panic("TODO enum type field"); + } else if (child_type->id == TypeTableEntryIdStruct) { + zig_panic("TODO struct type field"); + } else if (child_type->id == TypeTableEntryIdPureError) { + zig_panic("TODO error type field"); + } else if (child_type->id == TypeTableEntryIdInt) { + zig_panic("TODO integer type field"); + } else { + add_node_error(ira->codegen, read_field_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdNamespace) { + zig_panic("TODO namespace field access"); + } else { + add_node_error(ira->codegen, read_field_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + +static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { + IrInstruction *ptr = load_ptr_instruction->ptr->other; + TypeTableEntry *type_entry = ptr->type_entry; + if (type_entry->id == TypeTableEntryIdInvalid) { + return type_entry; + } else if (type_entry->id == TypeTableEntryIdPointer) { + TypeTableEntry *child_type = type_entry->data.pointer.child_type; + if (ptr->static_value.ok) { + ConstExprValue *pointee = ptr->static_value.data.x_ptr.ptr[0]; + if (pointee->ok) { + ConstExprValue *out_val = ir_build_const_from(ira, &load_ptr_instruction->base, + pointee->depends_on_compile_var); + *out_val = *pointee; + return child_type; + } + } + ir_build_load_ptr_from(&ira->new_irb, &load_ptr_instruction->base, ptr); + return child_type; + } else { + add_node_error(ira->codegen, load_ptr_instruction->base.source_node, + buf_sprintf("indirection requires pointer operand ('%s' invalid)", + buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + +static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { + IrInstruction *ptr = store_ptr_instruction->ptr->other; + if (ptr->type_entry->id == TypeTableEntryIdInvalid) + return ptr->type_entry; + + IrInstruction *value = store_ptr_instruction->value->other; + if (value->type_entry->id == TypeTableEntryIdInvalid) + return value->type_entry; + + TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type; + IrInstruction *casted_value = ir_get_casted_value(ira, value, child_type); + if (casted_value == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + if (ptr->static_value.ok && casted_value->static_value.ok) { + ConstExprValue *dest_val = ptr->static_value.data.x_ptr.ptr[0]; + if (dest_val->ok) { + *dest_val = casted_value->static_value; + return ir_analyze_void(ira, &store_ptr_instruction->base); + } + } + + if (ptr->static_value.ok) { + // This memory location is transforming from known at compile time to known at runtime. + // We must emit our own var ptr instruction. + ptr->static_value.ok = false; + IrInstruction *new_ptr_inst; + if (ptr->id == IrInstructionIdVarPtr) { + IrInstructionVarPtr *var_ptr_inst = (IrInstructionVarPtr *)ptr; + VariableTableEntry *var = var_ptr_inst->var; + new_ptr_inst = ir_build_var_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, var); + assert(var->mem_slot_index != SIZE_MAX); + ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; + mem_slot->ok = false; + } else if (ptr->id == IrInstructionIdFieldPtr) { + zig_panic("TODO"); + } else if (ptr->id == IrInstructionIdElemPtr) { + zig_panic("TODO"); + } else { + zig_unreachable(); + } + new_ptr_inst->type_entry = ptr->type_entry; + ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, new_ptr_inst, casted_value); + return ir_analyze_void(ira, &store_ptr_instruction->base); + } + + ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value); + return ira->codegen->builtin_types.entry_void; +} + +static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { + IrInstruction *expr_value = typeof_instruction->value->other; + TypeTableEntry *type_entry = expr_value->type_entry; + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + return type_entry; + case TypeTableEntryIdVar: + add_node_error(ira->codegen, expr_value->source_node, + buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNullLit: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdGenericFn: + 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: + { + ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false); + // TODO depends_on_compile_var should be set based on whether the type of the expression + // depends_on_compile_var. but we currently don't have a thing to tell us if the type of + // something depends on a compile var + out_val->data.x_type = type_entry; + + return ira->codegen->builtin_types.entry_type; + } + } + + zig_unreachable(); +} + +static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, + IrInstructionToPtrType *to_ptr_type_instruction) +{ + IrInstruction *type_value = to_ptr_type_instruction->value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_entry->id == TypeTableEntryIdInvalid) + return type_entry; + + TypeTableEntry *ptr_type; + if (type_entry->id == TypeTableEntryIdArray) { + ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false); + } else if (is_slice(type_entry)) { + ptr_type = type_entry->data.structure.fields[0].type_entry; + } else { + add_node_error(ira->codegen, to_ptr_type_instruction->base.source_node, + buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *out_val = ir_build_const_from(ira, &to_ptr_type_instruction->base, + type_value->static_value.depends_on_compile_var); + out_val->data.x_type = ptr_type; + return ira->codegen->builtin_types.entry_type; +} + +static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, + IrInstructionPtrTypeChild *ptr_type_child_instruction) +{ + IrInstruction *type_value = ptr_type_child_instruction->value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_entry->id == TypeTableEntryIdInvalid) + return type_entry; + + if (type_entry->id != TypeTableEntryIdPointer) { + add_node_error(ira->codegen, ptr_type_child_instruction->base.source_node, + buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *out_val = ir_build_const_from(ira, &ptr_type_child_instruction->base, + type_value->static_value.depends_on_compile_var); + out_val->data.x_type = type_entry->data.pointer.child_type; + return ira->codegen->builtin_types.entry_type; +} + +static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira, + IrInstructionSetFnTest *set_fn_test_instruction) +{ + IrInstruction *fn_value = set_fn_test_instruction->fn_value->other; + IrInstruction *is_test_value = set_fn_test_instruction->is_test->other; + + FnTableEntry *fn_entry = ir_resolve_fn(ira, fn_value); + if (!fn_entry) + return ira->codegen->builtin_types.entry_invalid; + + if (!ir_resolve_bool(ira, is_test_value, &fn_entry->is_test)) + return ira->codegen->builtin_types.entry_invalid; + + AstNode *source_node = set_fn_test_instruction->base.source_node; + if (fn_entry->fn_test_set_node) { + ErrorMsg *msg = add_node_error(ira->codegen, source_node, + buf_sprintf("function test attribute set twice")); + add_error_note(ira->codegen, msg, fn_entry->fn_test_set_node, buf_sprintf("first set here")); + return ira->codegen->builtin_types.entry_invalid; + } + fn_entry->fn_test_set_node = source_node; + + ira->codegen->test_fn_count += 1; + + ir_build_const_from(ira, &set_fn_test_instruction->base, false); + return ira->codegen->builtin_types.entry_void; +} + +static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { + switch (instruction->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdReturn: + return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); + case IrInstructionIdConst: + return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction); + case IrInstructionIdUnOp: + return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); + case IrInstructionIdBinOp: + return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); + case IrInstructionIdDeclVar: + return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); + case IrInstructionIdLoadPtr: + return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); + case IrInstructionIdStorePtr: + return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction); + case IrInstructionIdElemPtr: + return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction); + case IrInstructionIdVarPtr: + return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction); + case IrInstructionIdFieldPtr: + return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction); + case IrInstructionIdReadField: + return ir_analyze_instruction_read_field(ira, (IrInstructionReadField *)instruction); + case IrInstructionIdCall: + return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction); + case IrInstructionIdBr: + return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction); + case IrInstructionIdCondBr: + return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction); + case IrInstructionIdUnreachable: + return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction); + case IrInstructionIdPhi: + return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction); + case IrInstructionIdTypeOf: + return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction); + case IrInstructionIdToPtrType: + return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction); + case IrInstructionIdPtrTypeChild: + return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction); + case IrInstructionIdSetFnTest: + return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction); + case IrInstructionIdSwitchBr: + case IrInstructionIdCast: + case IrInstructionIdContainerInitList: + case IrInstructionIdContainerInitFields: + case IrInstructionIdStructFieldPtr: + zig_panic("TODO analyze more instructions"); + } + zig_unreachable(); +} + +static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { + TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction); + instruction->type_entry = instruction_type; + if (instruction->other) { + instruction->other->type_entry = instruction_type; + } else { + assert(instruction_type->id == TypeTableEntryIdInvalid || + instruction_type->id == TypeTableEntryIdUnreachable); + instruction->other = instruction; + } + return instruction_type; +} + +// This function attempts to evaluate IR code while doing type checking and other analysis. +// It emits a new IrExecutable which is partially evaluated IR code. +TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, + TypeTableEntry *expected_type, AstNode *expected_type_source_node) +{ + IrAnalyze ir_analyze_data = {}; + IrAnalyze *ira = &ir_analyze_data; + ira->codegen = codegen; + ira->explicit_return_type = expected_type; + + ira->old_irb.codegen = codegen; + ira->old_irb.exec = old_exec; + + ira->new_irb.codegen = codegen; + ira->new_irb.exec = new_exec; + + ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count; + ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count); + + IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); + IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb); + ir_ref_bb(new_entry_bb); + ira->old_irb.current_basic_block = old_entry_bb; + ira->new_irb.current_basic_block = new_entry_bb; + ira->block_queue_index = 0; + ira->instruction_index = 0; + + while (ira->block_queue_index < ira->old_bb_queue.length) { + IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); + + if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) { + ira->instruction_index += 1; + continue; + } + + TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction); + + // unreachable instructions do their own control flow. + if (return_type->id == TypeTableEntryIdUnreachable) + continue; + + ira->instruction_index += 1; + } + + return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items, + ira->implicit_return_type_list.length); +} + +bool ir_has_side_effects(IrInstruction *instruction) { + switch (instruction->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdBr: + case IrInstructionIdCondBr: + case IrInstructionIdSwitchBr: + case IrInstructionIdDeclVar: + case IrInstructionIdStorePtr: + case IrInstructionIdCall: + case IrInstructionIdReturn: + case IrInstructionIdUnreachable: + case IrInstructionIdSetFnTest: + return true; + case IrInstructionIdPhi: + case IrInstructionIdUnOp: + case IrInstructionIdBinOp: + case IrInstructionIdLoadPtr: + case IrInstructionIdConst: + case IrInstructionIdCast: + case IrInstructionIdContainerInitList: + case IrInstructionIdContainerInitFields: + case IrInstructionIdFieldPtr: + case IrInstructionIdElemPtr: + case IrInstructionIdVarPtr: + case IrInstructionIdTypeOf: + case IrInstructionIdToPtrType: + case IrInstructionIdPtrTypeChild: + case IrInstructionIdReadField: + case IrInstructionIdStructFieldPtr: + return false; + } + zig_unreachable(); +} + +IrInstruction *ir_exec_const_result(IrExecutable *exec) { + if (exec->basic_block_list.length != 1) + return nullptr; + + IrBasicBlock *bb = exec->basic_block_list.at(0); + if (bb->instruction_list.length != 1) + return nullptr; + + IrInstruction *only_inst = bb->instruction_list.at(0); + if (only_inst->id != IrInstructionIdReturn) + return nullptr; + + IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst; + IrInstruction *value = ret_inst->value; + assert(value->static_value.ok); + return value; +} + //static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context, // AstNode *node, const char *err_format, bool is_max) //{ @@ -3510,33 +4294,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio // //} // -//static TypeTableEntry *analyze_set_fn_test(CodeGen *g, ImportTableEntry *import, -// BlockContext *context, AstNode *node) -//{ -// AstNode **fn_node = &node->data.fn_call_expr.params.at(0); -// AstNode **value_node = &node->data.fn_call_expr.params.at(1); -// -// FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node); -// if (!fn_entry) { -// return g->builtin_types.entry_invalid; -// } -// -// bool ok = resolve_const_expr_bool(g, import, context, value_node, &fn_entry->is_test); -// if (!ok) { -// return g->builtin_types.entry_invalid; -// } -// -// if (fn_entry->fn_test_set_node) { -// ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function test attribute set twice")); -// add_error_note(g, msg, fn_entry->fn_test_set_node, buf_sprintf("first set here")); -// return g->builtin_types.entry_invalid; -// } -// fn_entry->fn_test_set_node = node; -// -// g->test_fn_count += 1; -// return g->builtin_types.entry_void; -//} -// //static TypeTableEntry *analyze_set_fn_no_inline(CodeGen *g, ImportTableEntry *import, // BlockContext *context, AstNode *node) //{ @@ -4188,762 +4945,2276 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio // } //} -static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) { - IrBasicBlock *old_dest_block = br_instruction->dest_block; - - // TODO detect backward jumps - - if (br_instruction->is_inline || old_dest_block->ref_count == 1) { - ir_inline_bb(ira, old_dest_block); - return ira->codegen->builtin_types.entry_unreachable; - } - - IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block); - ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb); - ir_finish_bb(ira); - return ira->codegen->builtin_types.entry_unreachable; -} - -static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { - TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type); - if (condition == ira->codegen->invalid_instruction) { - ir_finish_bb(ira); - return ira->codegen->builtin_types.entry_unreachable; - } - - // TODO detect backward jumps - if (condition->static_value.ok) { - IrBasicBlock *old_dest_block = condition->static_value.data.x_bool ? - cond_br_instruction->then_block : cond_br_instruction->else_block; - - if (cond_br_instruction->is_inline || old_dest_block->ref_count == 1) { - ir_inline_bb(ira, old_dest_block); - return ira->codegen->builtin_types.entry_unreachable; - } - } else if (cond_br_instruction->is_inline) { - add_node_error(ira->codegen, condition->source_node, - buf_sprintf("unable to evaluate constant expression")); - ir_finish_bb(ira); - return ira->codegen->builtin_types.entry_unreachable; - } - - IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block); - IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block); - ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, condition, new_then_block, new_else_block, false); - ir_finish_bb(ira); - return ira->codegen->builtin_types.entry_unreachable; -} - -static TypeTableEntry *ir_analyze_instruction_builtin_call(IrAnalyze *ira, - IrInstructionBuiltinCall *builtin_call_instruction) -{ - switch (builtin_call_instruction->fn->id) { - case BuiltinFnIdInvalid: - case BuiltinFnIdUnreachable: - case BuiltinFnIdTypeof: - zig_unreachable(); - case BuiltinFnIdMemcpy: - case BuiltinFnIdMemset: - case BuiltinFnIdSizeof: - case BuiltinFnIdAlignof: - case BuiltinFnIdMaxValue: - case BuiltinFnIdMinValue: - case BuiltinFnIdMemberCount: - case BuiltinFnIdAddWithOverflow: - case BuiltinFnIdSubWithOverflow: - case BuiltinFnIdMulWithOverflow: - case BuiltinFnIdShlWithOverflow: - case BuiltinFnIdCInclude: - case BuiltinFnIdCDefine: - case BuiltinFnIdCUndef: - case BuiltinFnIdCompileVar: - case BuiltinFnIdCompileErr: - case BuiltinFnIdConstEval: - case BuiltinFnIdCtz: - case BuiltinFnIdClz: - case BuiltinFnIdImport: - case BuiltinFnIdCImport: - case BuiltinFnIdErrName: - case BuiltinFnIdBreakpoint: - case BuiltinFnIdReturnAddress: - case BuiltinFnIdFrameAddress: - case BuiltinFnIdEmbedFile: - case BuiltinFnIdCmpExchange: - case BuiltinFnIdFence: - case BuiltinFnIdDivExact: - case BuiltinFnIdTruncate: - case BuiltinFnIdIntType: - case BuiltinFnIdSetFnTest: - case BuiltinFnIdSetFnVisible: - case BuiltinFnIdSetFnStaticEval: - case BuiltinFnIdSetFnNoInline: - case BuiltinFnIdSetDebugSafety: - zig_panic("TODO analyze more builtin functions"); - } - zig_unreachable(); -} - -static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira, - IrInstructionUnreachable *unreachable_instruction) -{ - ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base); - ir_finish_bb(ira); - return ira->codegen->builtin_types.entry_unreachable; -} - -static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) { - if (ira->const_predecessor_bb) { - for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { - IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; - if (predecessor != ira->const_predecessor_bb) - continue; - IrInstruction *value = phi_instruction->incoming_values[i]->other; - assert(value->type_entry); - if (value->static_value.ok) { - ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base, - value->static_value.depends_on_compile_var); - *out_val = value->static_value; - } else { - phi_instruction->base.other = value; - } - return value->type_entry; - } - zig_unreachable(); - } - - ZigList<IrBasicBlock*> new_incoming_blocks = {0}; - ZigList<IrInstruction*> new_incoming_values = {0}; - - for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { - IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; - if (predecessor->ref_count == 0) - continue; - - assert(predecessor->other); - new_incoming_blocks.append(predecessor->other); - - IrInstruction *old_value = phi_instruction->incoming_values[i]; - assert(old_value); - new_incoming_values.append(old_value->other); - } - assert(new_incoming_blocks.length != 0); - - if (new_incoming_blocks.length == 1) { - IrInstruction *first_value = new_incoming_values.at(0); - phi_instruction->base.other = first_value; - return first_value->type_entry; - } - - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, - new_incoming_values.items, new_incoming_values.length); - if (resolved_type->id == TypeTableEntryIdInvalid) - return resolved_type; - - ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length, - new_incoming_blocks.items, new_incoming_values.items); - return resolved_type; -} - -static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) { - VariableTableEntry *var = var_ptr_instruction->var; - if (var->type->id == TypeTableEntryIdInvalid) - return var->type; - - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var->type, false); - // TODO once the anlayze code is fully ported over to IR we won't need this SIZE_MAX thing. - if (var->mem_slot_index != SIZE_MAX) { - ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; - if (mem_slot->ok) { - ConstExprValue *out_val = ir_build_const_from(ira, &var_ptr_instruction->base, - mem_slot->depends_on_compile_var); - - out_val->data.x_ptr.len = 1; - out_val->data.x_ptr.is_c_str = false; - out_val->data.x_ptr.ptr = allocate<ConstExprValue *>(1); - out_val->data.x_ptr.ptr[0] = mem_slot; - return ptr_type; - } - } - - ir_build_var_ptr_from(&ira->new_irb, &var_ptr_instruction->base, var); - return ptr_type; -} - -static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { - IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other; - IrInstruction *elem_index = elem_ptr_instruction->elem_index->other; - - TypeTableEntry *array_type = array_ptr->type_entry; - TypeTableEntry *return_type; - - if (array_type->id == TypeTableEntryIdInvalid) { - return array_type; - } else if (array_type->id == TypeTableEntryIdArray) { - if (array_type->data.array.len == 0) { - add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, - buf_sprintf("out of bounds array access")); - } - TypeTableEntry *child_type = array_type->data.array.child_type; - return_type = get_pointer_to_type(ira->codegen, child_type, false); - } else if (array_type->id == TypeTableEntryIdPointer) { - return_type = array_type; - } else if (is_slice(array_type)) { - return_type = array_type->data.structure.fields[0].type_entry; - } else { - add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, - buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; - IrInstruction *casted_elem_index = ir_get_casted_value(ira, elem_index, usize); - if (casted_elem_index == ira->codegen->invalid_instruction) - return ira->codegen->builtin_types.entry_invalid; - - if (array_ptr->static_value.ok && casted_elem_index->static_value.ok) { - zig_panic("TODO compile time array access"); - } - - ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, casted_elem_index); - - return return_type; -} - -static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira, - TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction, - TypeTableEntry *container_type) -{ - if (!is_slice(bare_struct_type)) { - BlockContext *container_block_context = get_container_block_context(bare_struct_type); - assert(container_block_context); - 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) { - zig_panic("TODO member function call"); - } - } - add_node_error(ira->codegen, field_ptr_instruction->base.source_node, - buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); - return ira->codegen->builtin_types.entry_invalid; -} - - -static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *field_name, - IrInstructionFieldPtr *field_ptr_instruction, TypeTableEntry *container_type) -{ - IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; - TypeTableEntry *bare_type = container_ref_type(container_type); - if (!type_is_complete(bare_type)) { - resolve_container_type(ira->codegen, bare_type); - } - - if (bare_type->id == TypeTableEntryIdStruct) { - TypeStructField *field = find_struct_type_field(bare_type, field_name); - if (field) { - ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); - return get_pointer_to_type(ira->codegen, field->type_entry, false); - } else { - return ir_analyze_container_member_access_inner(ira, bare_type, field_name, - field_ptr_instruction, container_type); - } - } else if (bare_type->id == TypeTableEntryIdEnum) { - zig_panic("TODO enum field ptr"); - } else if (bare_type->id == TypeTableEntryIdUnion) { - zig_panic("TODO"); - } else { - zig_unreachable(); - } -} - -static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { - IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; - Buf *field_name = field_ptr_instruction->field_name; - - TypeTableEntry *container_type = container_ptr->type_entry; - if (container_type->id == TypeTableEntryIdInvalid) { - return container_type; - } else if (is_container_ref(container_type)) { - return ir_analyze_container_member_access(ira, field_name, field_ptr_instruction, container_type); - } else if (container_type->id == TypeTableEntryIdArray) { - if (buf_eql_str(field_name, "len")) { - add_node_error(ira->codegen, field_ptr_instruction->base.source_node, - buf_sprintf("pointer to array length not available")); - return ira->codegen->builtin_types.entry_invalid; - } else { - add_node_error(ira->codegen, field_ptr_instruction->base.source_node, - buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), - buf_ptr(&container_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (container_type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr); - - if (child_type->id == TypeTableEntryIdInvalid) { - return ira->codegen->builtin_types.entry_invalid; - } else if (child_type->id == TypeTableEntryIdEnum) { - zig_panic("TODO enum type field"); - } else if (child_type->id == TypeTableEntryIdStruct) { - zig_panic("TODO struct type field"); - } else if (child_type->id == TypeTableEntryIdPureError) { - zig_panic("TODO error type field"); - } else if (child_type->id == TypeTableEntryIdInt) { - zig_panic("TODO integer type field"); - } else { - add_node_error(ira->codegen, field_ptr_instruction->base.source_node, - buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (container_type->id == TypeTableEntryIdNamespace) { - zig_panic("TODO namespace field access"); - } else { - add_node_error(ira->codegen, field_ptr_instruction->base.source_node, - buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } -} - -static TypeTableEntry *ir_analyze_read_field_as_ptr_load(IrAnalyze *ira, - IrInstructionReadField *read_field_instruction) -{ - IrInstruction *old_field_ptr_inst = ir_build_field_ptr(&ira->old_irb, read_field_instruction->base.source_node, - read_field_instruction->container_ptr, read_field_instruction->field_name); - IrInstruction *old_load_ptr_inst = ir_build_load_ptr(&ira->old_irb, read_field_instruction->base.source_node, - old_field_ptr_inst); - ir_analyze_instruction(ira, old_field_ptr_inst); - TypeTableEntry *result_type = ir_analyze_instruction(ira, old_load_ptr_inst); - read_field_instruction->base.other = old_load_ptr_inst->other; - return result_type; -} - -static TypeTableEntry *ir_analyze_instruction_read_field(IrAnalyze *ira, - IrInstructionReadField *read_field_instruction) -{ - IrInstruction *container_ptr = read_field_instruction->container_ptr->other; - Buf *field_name = read_field_instruction->field_name; - - TypeTableEntry *container_type = container_ptr->type_entry; - if (container_type->id == TypeTableEntryIdInvalid) { - return container_type; - } else if (is_container_ref(container_type)) { - return ir_analyze_read_field_as_ptr_load(ira, read_field_instruction); - } else if (container_type->id == TypeTableEntryIdArray) { - if (buf_eql_str(field_name, "len")) { - return ir_analyze_const_usize(ira, &read_field_instruction->base, container_type->data.array.len, false); - } else { - add_node_error(ira->codegen, read_field_instruction->base.source_node, - buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), - buf_ptr(&container_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (container_type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr); - - if (child_type->id == TypeTableEntryIdInvalid) { - return ira->codegen->builtin_types.entry_invalid; - } else if (child_type->id == TypeTableEntryIdEnum) { - zig_panic("TODO enum type field"); - } else if (child_type->id == TypeTableEntryIdStruct) { - zig_panic("TODO struct type field"); - } else if (child_type->id == TypeTableEntryIdPureError) { - zig_panic("TODO error type field"); - } else if (child_type->id == TypeTableEntryIdInt) { - zig_panic("TODO integer type field"); - } else { - add_node_error(ira->codegen, read_field_instruction->base.source_node, - buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (container_type->id == TypeTableEntryIdNamespace) { - zig_panic("TODO namespace field access"); - } else { - add_node_error(ira->codegen, read_field_instruction->base.source_node, - buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } -} - -static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { - IrInstruction *ptr = load_ptr_instruction->ptr->other; - TypeTableEntry *type_entry = ptr->type_entry; - if (type_entry->id == TypeTableEntryIdInvalid) { - return type_entry; - } else if (type_entry->id == TypeTableEntryIdPointer) { - TypeTableEntry *child_type = type_entry->data.pointer.child_type; - if (ptr->static_value.ok) { - ConstExprValue *pointee = ptr->static_value.data.x_ptr.ptr[0]; - if (pointee->ok) { - ConstExprValue *out_val = ir_build_const_from(ira, &load_ptr_instruction->base, - pointee->depends_on_compile_var); - *out_val = *pointee; - return child_type; - } - } - ir_build_load_ptr_from(&ira->new_irb, &load_ptr_instruction->base, ptr); - return child_type; - } else { - add_node_error(ira->codegen, load_ptr_instruction->base.source_node, - buf_sprintf("indirection requires pointer operand ('%s' invalid)", - buf_ptr(&type_entry->name))); - return ira->codegen->builtin_types.entry_invalid; - } -} - -static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { - IrInstruction *ptr = store_ptr_instruction->ptr->other; - if (ptr->type_entry->id == TypeTableEntryIdInvalid) - return ptr->type_entry; - - IrInstruction *value = store_ptr_instruction->value->other; - if (value->type_entry->id == TypeTableEntryIdInvalid) - return value->type_entry; - - TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type; - IrInstruction *casted_value = ir_get_casted_value(ira, value, child_type); - if (casted_value == ira->codegen->invalid_instruction) - return ira->codegen->builtin_types.entry_invalid; - - if (ptr->static_value.ok && casted_value->static_value.ok) { - ConstExprValue *dest_val = ptr->static_value.data.x_ptr.ptr[0]; - if (dest_val->ok) { - *dest_val = casted_value->static_value; - return ir_analyze_void(ira, &store_ptr_instruction->base); - } - } - - if (ptr->static_value.ok) { - // This memory location is transforming from known at compile time to known at runtime. - // We must emit our own var ptr instruction. - ptr->static_value.ok = false; - IrInstruction *new_ptr_inst; - if (ptr->id == IrInstructionIdVarPtr) { - IrInstructionVarPtr *var_ptr_inst = (IrInstructionVarPtr *)ptr; - VariableTableEntry *var = var_ptr_inst->var; - new_ptr_inst = ir_build_var_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, var); - assert(var->mem_slot_index != SIZE_MAX); - ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; - mem_slot->ok = false; - } else if (ptr->id == IrInstructionIdFieldPtr) { - zig_panic("TODO"); - } else if (ptr->id == IrInstructionIdElemPtr) { - zig_panic("TODO"); - } else { - zig_unreachable(); - } - new_ptr_inst->type_entry = ptr->type_entry; - ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, new_ptr_inst, casted_value); - return ir_analyze_void(ira, &store_ptr_instruction->base); - } - ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value); - return ira->codegen->builtin_types.entry_void; -} -static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { - IrInstruction *expr_value = typeof_instruction->value->other; - TypeTableEntry *type_entry = expr_value->type_entry; - switch (type_entry->id) { - case TypeTableEntryIdInvalid: - return type_entry; - case TypeTableEntryIdVar: - add_node_error(ira->codegen, expr_value->source_node, - buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name))); - return ira->codegen->builtin_types.entry_invalid; - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: - case TypeTableEntryIdNamespace: - case TypeTableEntryIdBlock: - case TypeTableEntryIdGenericFn: - 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: - { - ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false); - // TODO depends_on_compile_var should be set based on whether the type of the expression - // depends_on_compile_var. but we currently don't have a thing to tell us if the type of - // something depends on a compile var - out_val->data.x_type = type_entry; - - return ira->codegen->builtin_types.entry_type; - } - } - - zig_unreachable(); -} - -static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, - IrInstructionToPtrType *to_ptr_type_instruction) -{ - IrInstruction *type_value = to_ptr_type_instruction->value->other; - TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - if (type_entry->id == TypeTableEntryIdInvalid) - return type_entry; - - TypeTableEntry *ptr_type; - if (type_entry->id == TypeTableEntryIdArray) { - ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false); - } else if (is_slice(type_entry)) { - ptr_type = type_entry->data.structure.fields[0].type_entry; - } else { - add_node_error(ira->codegen, to_ptr_type_instruction->base.source_node, - buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - ConstExprValue *out_val = ir_build_const_from(ira, &to_ptr_type_instruction->base, - type_value->static_value.depends_on_compile_var); - out_val->data.x_type = ptr_type; - return ira->codegen->builtin_types.entry_type; -} - -static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, - IrInstructionPtrTypeChild *ptr_type_child_instruction) -{ - IrInstruction *type_value = ptr_type_child_instruction->value->other; - TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - if (type_entry->id == TypeTableEntryIdInvalid) - return type_entry; - - if (type_entry->id != TypeTableEntryIdPointer) { - add_node_error(ira->codegen, ptr_type_child_instruction->base.source_node, - buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - ConstExprValue *out_val = ir_build_const_from(ira, &ptr_type_child_instruction->base, - type_value->static_value.depends_on_compile_var); - out_val->data.x_type = type_entry->data.pointer.child_type; - return ira->codegen->builtin_types.entry_type; -} - -static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { - switch (instruction->id) { - case IrInstructionIdInvalid: - zig_unreachable(); - case IrInstructionIdReturn: - return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); - case IrInstructionIdConst: - return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction); - case IrInstructionIdUnOp: - return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); - case IrInstructionIdBinOp: - return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); - case IrInstructionIdDeclVar: - return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); - case IrInstructionIdLoadPtr: - return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); - case IrInstructionIdStorePtr: - return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction); - case IrInstructionIdElemPtr: - return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction); - case IrInstructionIdVarPtr: - return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction); - case IrInstructionIdFieldPtr: - return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction); - case IrInstructionIdReadField: - return ir_analyze_instruction_read_field(ira, (IrInstructionReadField *)instruction); - case IrInstructionIdCall: - return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction); - case IrInstructionIdBr: - return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction); - case IrInstructionIdCondBr: - return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction); - case IrInstructionIdBuiltinCall: - return ir_analyze_instruction_builtin_call(ira, (IrInstructionBuiltinCall *)instruction); - case IrInstructionIdUnreachable: - return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction); - case IrInstructionIdPhi: - return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction); - case IrInstructionIdTypeOf: - return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction); - case IrInstructionIdToPtrType: - return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction); - case IrInstructionIdPtrTypeChild: - return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction); - case IrInstructionIdSwitchBr: - case IrInstructionIdCast: - case IrInstructionIdContainerInitList: - case IrInstructionIdContainerInitFields: - case IrInstructionIdStructFieldPtr: - zig_panic("TODO analyze more instructions"); - } - zig_unreachable(); -} - -static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { - TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction); - instruction->type_entry = instruction_type; - if (instruction->other) { - instruction->other->type_entry = instruction_type; - } else { - assert(instruction_type->id == TypeTableEntryIdInvalid || - instruction_type->id == TypeTableEntryIdUnreachable); - instruction->other = instruction; - } - return instruction_type; -} - -// This function attempts to evaluate IR code while doing type checking and other analysis. -// It emits a new IrExecutable which is partially evaluated IR code. -TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, - TypeTableEntry *expected_type, AstNode *expected_type_source_node) -{ - IrAnalyze ir_analyze_data = {}; - IrAnalyze *ira = &ir_analyze_data; - ira->codegen = codegen; - ira->explicit_return_type = expected_type; - - ira->old_irb.codegen = codegen; - ira->old_irb.exec = old_exec; - - ira->new_irb.codegen = codegen; - ira->new_irb.exec = new_exec; - - ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count; - ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count); - - IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); - IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb); - ir_ref_bb(new_entry_bb); - ira->old_irb.current_basic_block = old_entry_bb; - ira->new_irb.current_basic_block = new_entry_bb; - ira->block_queue_index = 0; - ira->instruction_index = 0; - - while (ira->block_queue_index < ira->old_bb_queue.length) { - IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); - - if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) { - ira->instruction_index += 1; - continue; - } - - TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction); - - // unreachable instructions do their own control flow. - if (return_type->id == TypeTableEntryIdUnreachable) - continue; - - ira->instruction_index += 1; - } - - return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items, - ira->implicit_return_type_list.length); -} +//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; +// +// if (struct_type->id == TypeTableEntryIdInvalid) { +// return struct_type; +// } else if (is_container_ref(struct_type)) { +// return analyze_container_member_access(g, field_name, node, struct_type); +// } 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); +// size_t 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 { +// 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 { +// 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 (size_t 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 && decl_node->owner != import) { +// 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 bool ir_builtin_call_has_side_effects(IrInstructionBuiltinCall *call_instruction) { - switch (call_instruction->fn->id) { - case BuiltinFnIdInvalid: - zig_unreachable(); - case BuiltinFnIdMemcpy: - case BuiltinFnIdMemset: - case BuiltinFnIdAddWithOverflow: - case BuiltinFnIdSubWithOverflow: - case BuiltinFnIdMulWithOverflow: - case BuiltinFnIdShlWithOverflow: - case BuiltinFnIdCInclude: - case BuiltinFnIdCDefine: - case BuiltinFnIdCUndef: - case BuiltinFnIdImport: - case BuiltinFnIdCImport: - case BuiltinFnIdBreakpoint: - case BuiltinFnIdEmbedFile: - case BuiltinFnIdCmpExchange: - case BuiltinFnIdFence: - case BuiltinFnIdUnreachable: - case BuiltinFnIdSetFnTest: - case BuiltinFnIdSetFnVisible: - case BuiltinFnIdSetFnStaticEval: - case BuiltinFnIdSetFnNoInline: - case BuiltinFnIdSetDebugSafety: - return true; - case BuiltinFnIdSizeof: - case BuiltinFnIdAlignof: - case BuiltinFnIdMaxValue: - case BuiltinFnIdMinValue: - case BuiltinFnIdMemberCount: - case BuiltinFnIdTypeof: - case BuiltinFnIdCompileVar: - case BuiltinFnIdCompileErr: - case BuiltinFnIdConstEval: - case BuiltinFnIdCtz: - case BuiltinFnIdClz: - case BuiltinFnIdErrName: - case BuiltinFnIdReturnAddress: - case BuiltinFnIdFrameAddress: - case BuiltinFnIdDivExact: - case BuiltinFnIdTruncate: - case BuiltinFnIdIntType: - return false; - } - zig_unreachable(); -} +//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->src_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, purpose); +// } 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; +//} -bool ir_has_side_effects(IrInstruction *instruction) { - switch (instruction->id) { - case IrInstructionIdInvalid: - zig_unreachable(); - case IrInstructionIdBr: - case IrInstructionIdCondBr: - case IrInstructionIdSwitchBr: - case IrInstructionIdDeclVar: - case IrInstructionIdStorePtr: - case IrInstructionIdCall: - case IrInstructionIdReturn: - case IrInstructionIdUnreachable: - return true; - case IrInstructionIdPhi: - case IrInstructionIdUnOp: - case IrInstructionIdBinOp: - case IrInstructionIdLoadPtr: - case IrInstructionIdConst: - case IrInstructionIdCast: - case IrInstructionIdContainerInitList: - case IrInstructionIdContainerInitFields: - case IrInstructionIdFieldPtr: - case IrInstructionIdElemPtr: - case IrInstructionIdVarPtr: - case IrInstructionIdTypeOf: - case IrInstructionIdToPtrType: - case IrInstructionIdPtrTypeChild: - case IrInstructionIdReadField: - case IrInstructionIdStructFieldPtr: - return false; - case IrInstructionIdBuiltinCall: - return ir_builtin_call_has_side_effects((IrInstructionBuiltinCall *)instruction); - } - zig_unreachable(); -} -IrInstruction *ir_exec_const_result(IrExecutable *exec) { - if (exec->basic_block_list.length != 1) - return nullptr; - IrBasicBlock *bb = exec->basic_block_list.at(0); - if (bb->instruction_list.length != 1) - return nullptr; +//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 BinOpTypeAssignTimesWrap: +// case BinOpTypeAssignDiv: +// case BinOpTypeAssignMod: +// case BinOpTypeAssignPlus: +// case BinOpTypeAssignPlusWrap: +// case BinOpTypeAssignMinus: +// case BinOpTypeAssignMinusWrap: +// case BinOpTypeAssignBitShiftLeft: +// case BinOpTypeAssignBitShiftLeftWrap: +// 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 BinOpTypeBitShiftLeftWrap: +// case BinOpTypeBitShiftRight: +// case BinOpTypeAdd: +// case BinOpTypeAddWrap: +// case BinOpTypeSub: +// case BinOpTypeSubWrap: +// case BinOpTypeMult: +// case BinOpTypeMultWrap: +// 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<ConstExprValue*>(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<ConstExprValue*>(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(); +//} - IrInstruction *only_inst = bb->instruction_list.at(0); - if (only_inst->id != IrInstructionIdReturn) - return nullptr; - IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst; - IrInstruction *value = ret_inst->value; - assert(value->static_value.ok); - return value; -} +//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 TypeTableEntryIdBlock: +// 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 TypeTableEntryIdNullLit: +// 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; +// +// case TypeTableEntryIdVar: +// zig_unreachable(); +// } +// +// 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_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_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 *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type, +// TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry) +//{ +// ErrorMsg *msg = add_node_error(g, node, +// buf_sprintf("function called as method of '%s', but first parameter is of type '%s'", +// buf_ptr(&container_type->name), +// buf_ptr(&expected_param_type->name))); +// if (fn_table_entry) { +// add_error_note(g, msg, fn_table_entry->proto_node, buf_sprintf("function declared here")); +// } +// return g->builtin_types.entry_invalid; +//} +// +//// 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 +// size_t struct_node_1_or_0 = struct_node ? 1 : 0; +// size_t 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); +// size_t call_param_count = node->data.fn_call_expr.params.length; +// size_t expect_arg_count = src_param_count - struct_node_1_or_0; +// +// bool ok_invocation = true; +// +// if (fn_type->data.fn.fn_type_id.is_var_args) { +// if (call_param_count < expect_arg_count) { +// ok_invocation = false; +// add_node_error(g, node, +// buf_sprintf("expected at least %zu arguments, got %zu", src_param_count, call_param_count)); +// } +// } else if (expect_arg_count != call_param_count) { +// ok_invocation = false; +// add_node_error(g, node, +// buf_sprintf("expected %zu arguments, got %zu", expect_arg_count, call_param_count)); +// } +// +// bool all_args_const_expr = true; +// +// if (struct_node) { +// Expr *struct_expr = get_resolved_expr(struct_node); +// ConstExprValue *struct_const_val = &struct_expr->const_val; +// if (!struct_const_val->ok) { +// all_args_const_expr = false; +// } +// +// FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[0]; +// TypeTableEntry *expected_param_type = param_info->type; +// TypeTableEntry *container_bare_type = container_ref_type(struct_expr->type_entry); +// if (is_container_ref(expected_param_type)) { +// TypeTableEntry *param_bare_type = container_ref_type(expected_param_type); +// if (param_bare_type != container_bare_type) { +// return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry); +// } +// } else { +// return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry); +// } +// } +// +// // 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. +// size_t next_type_i = struct_node_1_or_0; +// for (size_t call_i = 0; call_i < call_param_count; call_i += 1) { +// size_t 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 (fn_table_entry->proto_node->data.fn_proto.skip) { +// return g->builtin_types.entry_invalid; +// } +// } +// 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(g, context, node); +// if (fn_table_entry && fn_table_entry->want_pure == WantPureTrue) { +// return g->builtin_types.entry_invalid; +// } +// } +// +// // TODO +// //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 +// size_t struct_node_1_or_0 = (struct_node ? 1 : 0); +// size_t src_param_count = decl_node->data.fn_proto.params.length; +// size_t 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 %zu arguments, got %zu", src_param_count - struct_node_1_or_0, call_param_count)); +// return g->builtin_types.entry_invalid; +// } +// +// size_t inline_or_var_type_arg_count = decl_node->data.fn_proto.inline_or_var_type_arg_count; +// assert(inline_or_var_type_arg_count > 0); +// +// BlockContext *child_context = decl_node->owner->block_context; +// size_t next_generic_param_index = 0; +// +// GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1); +// generic_fn_type_id->decl_node = decl_node; +// generic_fn_type_id->generic_param_count = inline_or_var_type_arg_count; +// generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_or_var_type_arg_count); +// +// size_t next_impl_i = 0; +// for (size_t call_i = 0; call_i < call_param_count; call_i += 1) { +// size_t 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); +// +// 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; +// } +// +// bool is_var_type = (expected_param_type->id == TypeTableEntryIdVar); +// bool is_inline = generic_param_decl_node->data.param_decl.is_inline; +// if (!is_inline && !is_var_type) { +// next_impl_i += 1; +// continue; +// } +// +// +// AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i); +// TypeTableEntry *param_type = analyze_expression(g, import, parent_context, +// is_var_type ? nullptr : 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 (is_inline && !const_val->ok) { +// add_node_error(g, *param_node, +// buf_sprintf("unable to evaluate constant expression for inline parameter")); +// +// return g->builtin_types.entry_invalid; +// } +// +// VariableTableEntry *var = add_local_var_shadowable(g, generic_param_decl_node, decl_node->owner, child_context, +// generic_param_decl_node->data.param_decl.name, param_type, true, *param_node, true); +// // 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; +// +// GenericParamValue *generic_param_value = +// &generic_fn_type_id->generic_params[next_generic_param_index]; +// generic_param_value->type = param_type; +// generic_param_value->node = is_inline ? *param_node : nullptr; +// generic_param_value->impl_index = next_impl_i; +// next_generic_param_index += 1; +// +// if (!is_inline) { +// next_impl_i += 1; +// } +// } +// +// assert(next_generic_param_index == inline_or_var_type_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.inline_or_var_type_arg_count = 0; +// impl_decl_node->data.fn_proto.generic_proto_node = decl_node; +// +// // replace var arg types with actual types +// for (size_t generic_arg_i = 0; generic_arg_i < inline_or_var_type_arg_count; generic_arg_i += 1) { +// GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[generic_arg_i]; +// if (!generic_param_value->node) { +// size_t impl_i = generic_param_value->impl_index; +// AstNode *impl_param_decl_node = impl_decl_node->data.fn_proto.params.at(impl_i); +// assert(impl_param_decl_node->type == NodeTypeParamDecl); +// +// impl_param_decl_node->data.param_decl.type = create_ast_type_node(g, import, +// generic_param_value->type, impl_param_decl_node); +// normalize_parent_ptrs(impl_param_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<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params; +// +// size_t expected_param_count = generic_params->length; +// size_t 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 %zu arguments, got %zu", expected_param_count, actual_param_count)); +// return g->builtin_types.entry_invalid; +// } +// +// GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(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<GenericParamValue>(actual_param_count); +// +// BlockContext *child_context = decl_node->owner->block_context; +// for (size_t 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) { +// zig_panic("moved builtin fn call code to ir.cpp"); +// } +// +// 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) { +// zig_unreachable(); +// } 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_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; +// +// +// size_t prong_count = node->data.switch_expr.prongs.length; +// AstNode **peer_nodes = allocate<AstNode*>(prong_count); +// TypeTableEntry **peer_types = allocate<TypeTableEntry*>(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; +// } +// +// +// size_t *field_use_counts = nullptr; +// HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes = {}; +// if (expr_type->id == TypeTableEntryIdEnum) { +// field_use_counts = allocate<size_t>(expr_type->data.enumeration.src_field_count); +// } else if (expr_type->id == TypeTableEntryIdErrorUnion) { +// err_use_nodes.init(10); +// } +// +// size_t *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index; +// *const_chosen_prong_index = SIZE_MAX; +// AstNode *else_prong = nullptr; +// for (size_t 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 == SIZE_MAX && expr_val->ok) { +// *const_chosen_prong_index = prong_i; +// } +// } else { +// bool all_agree_on_var_type = true; +// var_type = nullptr; +// +// for (size_t 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 = find_enum_type_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 (size_t 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); +// +// 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); +// } +// // This must go after the analyze_expression for +// // prong_node->data.switch_prong.expr because of AST rewriting. +// peer_nodes[prong_i] = prong_node->data.switch_prong.expr; +// } +// +// if (expr_type->id == TypeTableEntryIdEnum && !else_prong) { +// for (uint32_t i = 0; i < expr_type->data.enumeration.src_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 != SIZE_MAX); +// +// *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 (!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: +// zig_panic("TODO moved to ir.cpp"); +// 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) { +// if (expected_return_type->id != TypeTableEntryIdErrorUnion && +// expected_return_type->id != TypeTableEntryIdPureError) +// { +// ErrorMsg *msg = add_node_error(g, node, +// buf_sprintf("%%return statement in function with return type '%s'", +// buf_ptr(&expected_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: +// { +// TypeTableEntry *expected_maybe_type; +// if (expected_type) { +// expected_maybe_type = get_maybe_type(g, expected_type); +// } else { +// expected_maybe_type = nullptr; +// } +// TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type, +// node->data.return_expr.expr); +// if (resolved_type->id == TypeTableEntryIdInvalid) { +// return resolved_type; +// } else if (resolved_type->id == TypeTableEntryIdMaybe) { +// if (expected_return_type->id != TypeTableEntryIdMaybe) { +// ErrorMsg *msg = add_node_error(g, node, +// buf_sprintf("?return statement in function with return type '%s'", +// buf_ptr(&expected_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.maybe.child_type; +// } else { +// add_node_error(g, node->data.return_expr.expr, +// buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name))); +// return g->builtin_types.entry_invalid; +// } +// } +// } +// zig_unreachable(); +//} +//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 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 = find_enum_type_field(enum_type, field_name); +// if (type_enum_field->type_entry->id == TypeTableEntryIdInvalid) { +// return g->builtin_types.entry_invalid; +// } +// +// 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 TypeTableEntry *analyze_container_member_access_inner(CodeGen *g, +// TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type) +//{ +// assert(node->type == NodeTypeFieldAccessExpr); +// if (!is_slice(bare_struct_type)) { +// BlockContext *container_block_context = get_container_block_context(bare_struct_type); +// assert(container_block_context); +// 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); +// } +// } +// } +// add_node_error(g, node, +// buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); +// return g->builtin_types.entry_invalid; +//} +// +//static TypeTableEntry *analyze_container_member_access(CodeGen *g, +// Buf *field_name, AstNode *node, TypeTableEntry *struct_type) +//{ +// TypeTableEntry *bare_type = container_ref_type(struct_type); +// if (!type_is_complete(bare_type)) { +// resolve_container_type(g, bare_type); +// } +// +// node->data.field_access_expr.bare_container_type = bare_type; +// +// if (bare_type->id == TypeTableEntryIdStruct) { +// node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_type, field_name); +// if (node->data.field_access_expr.type_struct_field) { +// return node->data.field_access_expr.type_struct_field->type_entry; +// } else { +// return analyze_container_member_access_inner(g, bare_type, field_name, +// node, struct_type); +// } +// } else if (bare_type->id == TypeTableEntryIdEnum) { +// node->data.field_access_expr.type_enum_field = find_enum_type_field(bare_type, field_name); +// if (node->data.field_access_expr.type_enum_field) { +// return node->data.field_access_expr.type_enum_field->type_entry; +// } else { +// return analyze_container_member_access_inner(g, bare_type, field_name, +// node, struct_type); +// } +// } else if (bare_type->id == TypeTableEntryIdUnion) { +// zig_panic("TODO"); +// } else { +// zig_unreachable(); +// } +//} +// +//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, LValPurpose purpose) +//{ +// 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) { +// if (array_type->data.pointer.is_const && purpose == LValPurposeAssign) { +// add_node_error(g, node, buf_sprintf("cannot assign to constant")); +// return g->builtin_types.entry_invalid; +// } +// return_type = array_type->data.pointer.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; +// if (pointer_type->data.pointer.is_const && purpose == LValPurposeAssign) { +// add_node_error(g, node, buf_sprintf("cannot assign to constant")); +// return g->builtin_types.entry_invalid; +// } +// return_type = pointer_type->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 *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<ConstExprValue*>(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_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_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_pointer_only(g, import, context, +// node->data.array_type.child_type, true); +// +// 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) { +// child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type); +// 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 { +// TypeTableEntry *slice_type = get_slice_type(g, child_type, node->data.array_type.is_const); +// return resolve_expr_const_val_as_type(g, node, slice_type, 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_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 *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 (size_t 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<LabelTableEntry>(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(g, context, node); +// +// node->data.asm_expr.return_count = 0; +// TypeTableEntry *return_type = g->builtin_types.entry_void; +// for (size_t 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 (size_t 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_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 TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, +// TypeTableEntry *expected_type, AstNode *node, bool pointer_only) +//{ +// 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(g, context, node); +// add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); +// return g->builtin_types.entry_invalid; +//} +// +//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_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(g, context, source_node); +// } +// if (var->src_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_null_literal_expr(CodeGen *g, ImportTableEntry *import, +// BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) +//{ +// assert(node->type == NodeTypeNullLiteral); +// +// ConstExprValue *const_val = &get_resolved_expr(node)->const_val; +// const_val->ok = true; +// +// return g->builtin_types.entry_null; +//} +// +//static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, +// TypeTableEntry *expected_type, AstNode *node) +//{ +// assert(node->type == NodeTypeUndefinedLiteral); +// +// Expr *expr = get_resolved_expr(node); +// ConstExprValue *const_val = &expr->const_val; +// +// const_val->ok = true; +// const_val->special = ConstValSpecialUndef; +// +// return expected_type ? expected_type : g->builtin_types.entry_undef; +//} +// +//static TypeTableEntry *analyze_zeroes_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->special = ConstValSpecialZeroes; +// +// 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) +//{ +// return resolve_expr_const_val_as_bignum(g, node, expected_type, node->data.number_literal.bignum, 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, nullptr); +// +// if (type_entry->id == TypeTableEntryIdInvalid) { +// return type_entry; +// } +// +// return resolve_expr_const_val_as_type(g, node, type_entry, false); +//} +// +//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->src_is_const && var->type->deep_const; +//} +// +//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")); +// } +//} +// diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 52535defe8..5a0f33b317 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -78,6 +78,12 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const fprintf(irp->f, "%s", buf_ptr(&fn_entry->symbol_name)); break; } + case TypeTableEntryIdBlock: + { + AstNode *node = const_val->data.x_block->node; + fprintf(irp->f, "(scope:%zu:%zu)", node->line + 1, node->column + 1); + break; + } case TypeTableEntryIdVar: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: @@ -91,7 +97,6 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const case TypeTableEntryIdUnion: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: - case TypeTableEntryIdBlock: case TypeTableEntryIdGenericFn: zig_panic("TODO render more constant types in IR printer"); } @@ -263,18 +268,6 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { fprintf(irp->f, ")"); } -static void ir_print_builtin_call(IrPrint *irp, IrInstructionBuiltinCall *call_instruction) { - fprintf(irp->f, "@%s(", buf_ptr(&call_instruction->fn->name)); - for (size_t i = 0; i < call_instruction->fn->param_count; i += 1) { - IrInstruction *arg = call_instruction->args[i]; - if (i != 0) - fprintf(irp->f, ", "); - ir_print_other_instruction(irp, arg); - } - fprintf(irp->f, ")"); -} - - static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) { const char *inline_kw = cond_br_instruction->is_inline ? "inline " : ""; fprintf(irp->f, "%sif (", inline_kw); @@ -393,6 +386,14 @@ static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr fprintf(irp->f, ")"); } +static void ir_print_set_fn_test(IrPrint *irp, IrInstructionSetFnTest *instruction) { + fprintf(irp->f, "@setFnTest("); + ir_print_other_instruction(irp, instruction->fn_value); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->is_test); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -425,9 +426,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBr: ir_print_br(irp, (IrInstructionBr *)instruction); break; - case IrInstructionIdBuiltinCall: - ir_print_builtin_call(irp, (IrInstructionBuiltinCall *)instruction); - break; case IrInstructionIdPhi: ir_print_phi(irp, (IrInstructionPhi *)instruction); break; @@ -470,6 +468,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdStructFieldPtr: ir_print_struct_field_ptr(irp, (IrInstructionStructFieldPtr *)instruction); break; + case IrInstructionIdSetFnTest: + ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction); + break; case IrInstructionIdSwitchBr: zig_panic("TODO print more IR instructions"); } diff --git a/src/parseh.cpp b/src/parseh.cpp index 1a581f3a23..f44875ff03 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -230,9 +230,7 @@ static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *chi } static AstNode *make_type_node(Context *c, TypeTableEntry *type_entry) { - AstNode *node = create_node(c, NodeTypeSymbol); - node->data.symbol_expr.override_type_entry = type_entry; - return node; + zig_panic("TODO bypass AST in parseh"); } static AstNode *create_fn_proto_node(Context *c, Buf *name, TypeTableEntry *fn_type) { |
