diff options
Diffstat (limited to 'src/analyze.cpp')
| -rw-r--r-- | src/analyze.cpp | 3517 |
1 files changed, 306 insertions, 3211 deletions
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, |
