aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2016-11-04 15:36:30 -0400
committerAndrew Kelley <superjoe30@gmail.com>2016-11-04 15:36:30 -0400
commita2e32939305e470ce3d32c9d2667d3083158ddb3 (patch)
treefef679349a0c2d2e25072e2960e39b2ec257fa07 /src
parentbc6c33b1b64822b0667ab88b73f4b5c4b302154f (diff)
downloadzig-a2e32939305e470ce3d32c9d2667d3083158ddb3.tar.gz
zig-a2e32939305e470ce3d32c9d2667d3083158ddb3.zip
WIP moving all analysis to IR
Diffstat (limited to 'src')
-rw-r--r--src/all_types.hpp18
-rw-r--r--src/analyze.cpp3517
-rw-r--r--src/analyze.hpp4
-rw-r--r--src/ast_render.cpp32
-rw-r--r--src/codegen.cpp4
-rw-r--r--src/ir.cpp3913
-rw-r--r--src/ir_print.cpp33
-rw-r--r--src/parseh.cpp4
8 files changed, 3430 insertions, 4095 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 5495e25d39..8cc943f30b 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -738,8 +738,6 @@ struct AstNodeSymbolExpr {
// populated by semantic analyzer
Expr resolved_expr;
- // set this to instead of analyzing the node, pretend it's a type entry and it's this one.
- TypeTableEntry *override_type_entry;
TypeEnumField *enum_field;
uint32_t err_value;
};
@@ -1439,7 +1437,6 @@ enum IrInstructionId {
IrInstructionIdElemPtr,
IrInstructionIdVarPtr,
IrInstructionIdCall,
- IrInstructionIdBuiltinCall,
IrInstructionIdConst,
IrInstructionIdReturn,
IrInstructionIdCast,
@@ -1449,6 +1446,7 @@ enum IrInstructionId {
IrInstructionIdTypeOf,
IrInstructionIdToPtrType,
IrInstructionIdPtrTypeChild,
+ IrInstructionIdSetFnTest,
};
struct IrInstruction {
@@ -1630,13 +1628,6 @@ struct IrInstructionCall {
IrInstruction **args;
};
-struct IrInstructionBuiltinCall {
- IrInstruction base;
-
- BuiltinFnEntry *fn;
- IrInstruction **args;
-};
-
struct IrInstructionConst {
IrInstruction base;
};
@@ -1698,6 +1689,13 @@ struct IrInstructionPtrTypeChild {
IrInstruction *value;
};
+struct IrInstructionSetFnTest {
+ IrInstruction base;
+
+ IrInstruction *fn_value;
+ IrInstruction *is_test;
+};
+
enum LValPurpose {
LValPurposeNone,
LValPurposeAssign,
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 8a0fe19098..50e690b9bf 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -17,41 +17,9 @@
#include "parser.hpp"
#include "zig_llvm.hpp"
-static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node);
-static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only);
-static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, TypeTableEntry *expected_type, AstNode *node);
+static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type);
static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type);
-static TypeTableEntry *unwrapped_node_type(AstNode *node);
-static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node, Buf *err_name);
-static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node);
-static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn,
- bool depends_on_compile_var);
-static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node,
- TypeTableEntry *type_entry, bool depends_on_compile_var);
-static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type,
- bool depends_on_compile_var);
-static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
- TypeTableEntry *expected_type, uint64_t x, bool depends_on_compile_var);
-static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value,
- bool depends_on_compile_var);
-static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
- bool pointer_only, BlockContext *block_context, bool depends_on_compile_var);
-static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *source_node,
- AstNodeVariableDeclaration *variable_declaration,
- bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr);
static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node);
-static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
-static void resolve_use_decl(CodeGen *g, AstNode *node);
-static void preview_use_decl(CodeGen *g, AstNode *node);
-static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
- BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const,
- AstNode *val_node);
AstNode *first_executing_node(AstNode *node) {
switch (node->type) {
@@ -868,39 +836,52 @@ TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry) {
}
}
-// If the node does not have a constant expression value with a metatype, generates an error
-// and returns invalid type. Otherwise, returns the type of the constant expression value.
-// Must be called after analyze_expression on the same node.
-static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
- if (node->type == NodeTypeSymbol && node->data.symbol_expr.override_type_entry) {
- return node->data.symbol_expr.override_type_entry;
+static IrInstruction *analyze_const_value(CodeGen *g, BlockContext *scope, AstNode *node,
+ TypeTableEntry *expected_type)
+{
+ IrExecutable ir_executable = {0};
+ IrExecutable analyzed_executable = {0};
+ IrInstruction *pass1 = ir_gen(g, node, scope, &ir_executable);
+
+ if (pass1->type_entry->id == TypeTableEntryIdInvalid)
+ return g->invalid_instruction;
+
+ if (g->verbose) {
+ fprintf(stderr, "{\n");
+ ir_print(stderr, &ir_executable, 4);
+ fprintf(stderr, "}\n");
}
- Expr *expr = get_resolved_expr(node);
- assert(expr->type_entry);
- if (expr->type_entry->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (expr->type_entry->id == TypeTableEntryIdMetaType) {
- // OK
- } else {
- add_node_error(g, node, buf_sprintf("expected type, found expression"));
- return g->builtin_types.entry_invalid;
+ TypeTableEntry *result_type = ir_analyze(g, &ir_executable, &analyzed_executable, expected_type, node);
+ if (result_type->id == TypeTableEntryIdInvalid)
+ return g->invalid_instruction;
+
+ if (g->verbose) {
+ fprintf(stderr, "{ // (analyzed)\n");
+ ir_print(stderr, &analyzed_executable, 4);
+ fprintf(stderr, "}\n");
}
- ConstExprValue *const_val = &expr->const_val;
- if (!const_val->ok) {
+ IrInstruction *result = ir_exec_const_result(&analyzed_executable);
+ if (!result) {
add_node_error(g, node, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
+ return g->invalid_instruction;
}
- return const_val->data.x_type;
+ return result;
}
static TypeTableEntry *analyze_type_expr_pointer_only(CodeGen *g, ImportTableEntry *import,
BlockContext *context, AstNode *node, bool pointer_only)
{
- AstNode **node_ptr = node->parent_field;
- analyze_expression_pointer_only(g, import, context, nullptr, *node_ptr, pointer_only);
- return resolve_type(g, *node_ptr);
+ if (pointer_only)
+ zig_panic("TODO");
+
+ IrInstruction *result = analyze_const_value(g, context, node, g->builtin_types.entry_type);
+ if (result->type_entry->id == TypeTableEntryIdInvalid)
+ return g->builtin_types.entry_invalid;
+
+ assert(result->static_value.ok);
+ return result->static_value.data.x_type;
}
// Calls analyze_expression on node, and then resolve_type.
@@ -1607,6 +1588,34 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
}
}
+static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
+ AstNode *node, Buf *name)
+{
+ assert(import);
+
+ TopLevelDecl *tld = get_as_top_level_decl(node);
+ tld->import = import;
+ tld->name = name;
+
+ bool want_to_resolve = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport);
+ bool is_generic_container = (node->type == NodeTypeContainerDecl &&
+ node->data.struct_decl.generic_params.length > 0);
+ if (want_to_resolve && !is_generic_container) {
+ g->resolve_queue.append(node);
+ }
+
+ node->block_context = block_context;
+
+ auto entry = block_context->decl_table.maybe_get(name);
+ if (entry) {
+ AstNode *other_decl_node = entry->value;
+ ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name)));
+ add_error_note(g, msg, other_decl_node, buf_sprintf("previous definition is here"));
+ } else {
+ block_context->decl_table.put(name, node);
+ }
+}
+
static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
assert(node->type == NodeTypeContainerDecl);
@@ -1629,30 +1638,27 @@ static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext
}
}
-static void resolve_struct_instance(CodeGen *g, ImportTableEntry *import, AstNode *node) {
- TypeTableEntry *type_entry = node->data.struct_decl.type_entry;
- assert(type_entry);
+static void count_inline_and_var_args(AstNode *proto_node) {
+ assert(proto_node->type == NodeTypeFnProto);
- // struct/enum member fns will get resolved independently
+ size_t *inline_arg_count = &proto_node->data.fn_proto.inline_arg_count;
+ size_t *inline_or_var_type_arg_count = &proto_node->data.fn_proto.inline_or_var_type_arg_count;
- switch (node->data.struct_decl.kind) {
- case ContainerKindStruct:
- resolve_struct_type(g, import, type_entry);
- break;
- case ContainerKindEnum:
- resolve_enum_type(g, import, type_entry);
- break;
- case ContainerKindUnion:
- resolve_union_type(g, import, type_entry);
- break;
- }
-}
+ *inline_arg_count = 0;
+ *inline_or_var_type_arg_count = 0;
-static void resolve_struct_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
- if (node->data.struct_decl.generic_params.length > 0) {
- return preview_generic_fn_proto(g, import, node);
- } else {
- return resolve_struct_instance(g, import, node);
+ // TODO run these nodes through the type analysis system rather than looking for
+ // specialized ast nodes. this would get fooled by `{var}` instead of `var` which
+ // is supposed to be equivalent
+ for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
+ AstNode *param_node = proto_node->data.fn_proto.params.at(i);
+ assert(param_node->type == NodeTypeParamDecl);
+ if (param_node->data.param_decl.is_inline) {
+ *inline_arg_count += 1;
+ *inline_or_var_type_arg_count += 1;
+ } else if (param_node->data.param_decl.type->type == NodeTypeVarLiteral) {
+ *inline_or_var_type_arg_count += 1;
+ }
}
}
@@ -1680,6 +1686,137 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) {
node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk;
}
+static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
+ switch (node->type) {
+ case NodeTypeRoot:
+ for (size_t i = 0; i < import->root->data.root.top_level_decls.length; i += 1) {
+ AstNode *child = import->root->data.root.top_level_decls.at(i);
+ scan_decls(g, import, context, child);
+ }
+ break;
+ case NodeTypeContainerDecl:
+ {
+ Buf *name = node->data.struct_decl.name;
+ add_top_level_decl(g, import, context, node, name);
+ if (node->data.struct_decl.generic_params.length == 0) {
+ scan_struct_decl(g, import, context, node);
+ }
+ }
+ break;
+ case NodeTypeFnDef:
+ node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node;
+ scan_decls(g, import, context, node->data.fn_def.fn_proto);
+ break;
+ case NodeTypeVariableDeclaration:
+ {
+ Buf *name = node->data.variable_declaration.symbol;
+ add_top_level_decl(g, import, context, node, name);
+ break;
+ }
+ case NodeTypeTypeDecl:
+ {
+ Buf *name = node->data.type_decl.symbol;
+ add_top_level_decl(g, import, context, node, name);
+ break;
+ }
+ case NodeTypeFnProto:
+ {
+ // if the name is missing, we immediately announce an error
+ Buf *fn_name = node->data.fn_proto.name;
+ if (buf_len(fn_name) == 0) {
+ node->data.fn_proto.skip = true;
+ add_node_error(g, node, buf_sprintf("missing function name"));
+ break;
+ }
+ count_inline_and_var_args(node);
+
+ add_top_level_decl(g, import, context, node, fn_name);
+ break;
+ }
+ case NodeTypeUse:
+ {
+ TopLevelDecl *tld = get_as_top_level_decl(node);
+ tld->import = import;
+ node->block_context = context;
+ g->use_queue.append(node);
+ tld->import->use_decls.append(node);
+ break;
+ }
+ case NodeTypeErrorValueDecl:
+ // error value declarations do not depend on other top level decls
+ preview_error_value_decl(g, node);
+ break;
+ case NodeTypeParamDecl:
+ case NodeTypeFnDecl:
+ case NodeTypeReturnExpr:
+ case NodeTypeDefer:
+ case NodeTypeBlock:
+ case NodeTypeBinOpExpr:
+ case NodeTypeUnwrapErrorExpr:
+ case NodeTypeFnCallExpr:
+ case NodeTypeArrayAccessExpr:
+ case NodeTypeSliceExpr:
+ case NodeTypeNumberLiteral:
+ case NodeTypeStringLiteral:
+ case NodeTypeCharLiteral:
+ case NodeTypeBoolLiteral:
+ case NodeTypeNullLiteral:
+ case NodeTypeUndefinedLiteral:
+ case NodeTypeZeroesLiteral:
+ case NodeTypeThisLiteral:
+ case NodeTypeSymbol:
+ case NodeTypePrefixOpExpr:
+ case NodeTypeIfBoolExpr:
+ case NodeTypeIfVarExpr:
+ case NodeTypeWhileExpr:
+ case NodeTypeForExpr:
+ case NodeTypeSwitchExpr:
+ case NodeTypeSwitchProng:
+ case NodeTypeSwitchRange:
+ case NodeTypeLabel:
+ case NodeTypeGoto:
+ case NodeTypeBreak:
+ case NodeTypeContinue:
+ case NodeTypeAsmExpr:
+ case NodeTypeFieldAccessExpr:
+ case NodeTypeStructField:
+ case NodeTypeContainerInitExpr:
+ case NodeTypeStructValueField:
+ case NodeTypeArrayType:
+ case NodeTypeErrorType:
+ case NodeTypeTypeLiteral:
+ case NodeTypeVarLiteral:
+ zig_unreachable();
+ }
+}
+
+static void resolve_struct_instance(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+ TypeTableEntry *type_entry = node->data.struct_decl.type_entry;
+ assert(type_entry);
+
+ // struct/enum member fns will get resolved independently
+
+ switch (node->data.struct_decl.kind) {
+ case ContainerKindStruct:
+ resolve_struct_type(g, import, type_entry);
+ break;
+ case ContainerKindEnum:
+ resolve_enum_type(g, import, type_entry);
+ break;
+ case ContainerKindUnion:
+ resolve_union_type(g, import, type_entry);
+ break;
+ }
+}
+
+static void resolve_struct_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+ if (node->data.struct_decl.generic_params.length > 0) {
+ return preview_generic_fn_proto(g, import, node);
+ } else {
+ return resolve_struct_instance(g, import, node);
+ }
+}
+
TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
TypeTableEntry *underlying_type = get_underlying_type(type_entry);
switch (underlying_type->id) {
@@ -1718,6 +1855,72 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
zig_unreachable();
}
+// Set name to nullptr to make the variable anonymous (not visible to programmer).
+// TODO merge with definition of add_local_var in ir.cpp
+static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
+ BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node,
+ bool shadowable)
+{
+ VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
+ variable_entry->type = type_entry;
+ variable_entry->block_context = context;
+ variable_entry->import = import;
+ variable_entry->shadowable = shadowable;
+ variable_entry->mem_slot_index = SIZE_MAX;
+
+ if (name) {
+ buf_init_from_buf(&variable_entry->name, name);
+
+ if (type_entry->id != TypeTableEntryIdInvalid) {
+ VariableTableEntry *existing_var = find_variable(g, context, name);
+ if (existing_var && !existing_var->shadowable) {
+ ErrorMsg *msg = add_node_error(g, source_node,
+ buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
+ add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
+ variable_entry->type = g->builtin_types.entry_invalid;
+ } else {
+ auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
+ if (primitive_table_entry) {
+ TypeTableEntry *type = primitive_table_entry->value;
+ add_node_error(g, source_node,
+ buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
+ variable_entry->type = g->builtin_types.entry_invalid;
+ } else {
+ AstNode *decl_node = find_decl(context, name);
+ if (decl_node && decl_node->type != NodeTypeVariableDeclaration) {
+ ErrorMsg *msg = add_node_error(g, source_node,
+ buf_sprintf("redefinition of '%s'", buf_ptr(name)));
+ add_error_note(g, msg, decl_node, buf_sprintf("previous definition is here"));
+ variable_entry->type = g->builtin_types.entry_invalid;
+ }
+ }
+ }
+ }
+
+ context->var_table.put(&variable_entry->name, variable_entry);
+ } else {
+ // TODO replace _anon with @anon and make sure all tests still pass
+ buf_init_from_str(&variable_entry->name, "_anon");
+ }
+ if (context->fn_entry) {
+ context->fn_entry->variable_list.append(variable_entry);
+ }
+
+ variable_entry->src_is_const = is_const;
+ variable_entry->gen_is_const = is_const;
+ variable_entry->decl_node = source_node;
+ variable_entry->val_node = val_node;
+
+
+ return variable_entry;
+}
+
+static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
+ BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node)
+{
+ return add_local_var_shadowable(g, source_node, import, context, name, type_entry, is_const, val_node, false);
+}
+
static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
assert(node->type == NodeTypeVariableDeclaration);
@@ -1739,53 +1942,28 @@ static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node
if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) {
implicit_type = explicit_type;
} else if (var_decl->expr) {
- IrExecutable ir_executable = {0};
- IrExecutable analyzed_executable = {0};
- IrInstruction *result = ir_gen(g, var_decl->expr, scope, &ir_executable);
- if (result == g->invalid_instruction) {
- // ignore the poison value
- implicit_type = g->builtin_types.entry_invalid;
- } else {
- if (g->verbose) {
- fprintf(stderr, "var %s = {\n", buf_ptr(var_decl->symbol));
- ir_print(stderr, &ir_executable, 4);
- fprintf(stderr, "}\n");
- }
- implicit_type = ir_analyze(g, &ir_executable, &analyzed_executable,
- explicit_type, var_decl->type);
- if (g->verbose) {
- fprintf(stderr, "var %s = { // (analyzed)\n", buf_ptr(var_decl->symbol));
- ir_print(stderr, &analyzed_executable, 4);
- fprintf(stderr, "}\n");
- }
+ IrInstruction *result = analyze_const_value(g, scope, var_decl->expr, explicit_type);
+ assert(result);
+ implicit_type = result->type_entry;
- if (implicit_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, node,
- buf_sprintf("variable initialization is unreachable"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if ((!is_const || is_export) &&
- (implicit_type->id == TypeTableEntryIdNumLitFloat ||
- implicit_type->id == TypeTableEntryIdNumLitInt))
- {
- add_node_error(g, node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
- add_node_error(g, node, buf_sprintf("variable of type 'type' must be constant"));
- implicit_type = g->builtin_types.entry_invalid;
- }
- if (implicit_type->id != TypeTableEntryIdInvalid) {
- Expr *expr = get_resolved_expr(var_decl->expr);
- IrInstruction *result = ir_exec_const_result(&analyzed_executable);
- if (result) {
- assert(result->static_value.ok);
- expr->const_val = result->static_value;
- expr->type_entry = result->type_entry;
- } else {
- add_node_error(g, first_executing_node(var_decl->expr),
- buf_sprintf("global variable initializer requires constant expression"));
- implicit_type = g->builtin_types.entry_invalid;
- }
- }
+ if (implicit_type->id == TypeTableEntryIdUnreachable) {
+ add_node_error(g, node, buf_sprintf("variable initialization is unreachable"));
+ implicit_type = g->builtin_types.entry_invalid;
+ } else if ((!is_const || is_export) &&
+ (implicit_type->id == TypeTableEntryIdNumLitFloat ||
+ implicit_type->id == TypeTableEntryIdNumLitInt))
+ {
+ add_node_error(g, node, buf_sprintf("unable to infer variable type"));
+ implicit_type = g->builtin_types.entry_invalid;
+ } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
+ add_node_error(g, node, buf_sprintf("variable of type 'type' must be constant"));
+ implicit_type = g->builtin_types.entry_invalid;
+ }
+ if (implicit_type->id != TypeTableEntryIdInvalid) {
+ Expr *expr = get_resolved_expr(var_decl->expr);
+ assert(result->static_value.ok);
+ expr->const_val = result->static_value;
+ expr->type_entry = result->type_entry;
}
} else if (!is_extern) {
add_node_error(g, node, buf_sprintf("variables must be initialized"));
@@ -1906,30 +2084,6 @@ void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) {
tld->dep_loop_flag = false;
}
-static FnTableEntry *get_context_fn_entry(BlockContext *context) {
- assert(context->fn_entry);
- return context->fn_entry;
-}
-
-static TypeTableEntry *unwrapped_node_type(AstNode *node) {
- Expr *expr = get_resolved_expr(node);
- if (expr->type_entry->id == TypeTableEntryIdInvalid) {
- return expr->type_entry;
- }
- assert(expr->type_entry->id == TypeTableEntryIdMetaType);
- ConstExprValue *const_val = &expr->const_val;
- assert(const_val->ok);
- return const_val->data.x_type;
-}
-
-static TypeTableEntry *get_return_type(BlockContext *context) {
- FnTableEntry *fn_entry = get_context_fn_entry(context);
- AstNode *fn_proto_node = fn_entry->proto_node;
- assert(fn_proto_node->type == NodeTypeFnProto);
- AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
- return unwrapped_node_type(return_type_node);
-}
-
static bool type_has_codegen_value(TypeTableEntry *type_entry) {
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
@@ -1968,18 +2122,6 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {
zig_unreachable();
}
-static void add_global_const_expr(CodeGen *g, AstNode *expr_node) {
- Expr *expr = get_resolved_expr(expr_node);
- if (expr->const_val.ok &&
- type_has_codegen_value(expr->type_entry) &&
- !expr->has_global_const &&
- type_has_bits(expr->type_entry))
- {
- g->global_const_list.append(expr_node);
- expr->has_global_const = true;
- }
-}
-
static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTableEntry *other_type) {
TypeTableEntry *other_type_underlying = get_underlying_type(other_type);
@@ -2105,85 +2247,6 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *
return false;
}
-static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node,
- AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count)
-{
- TypeTableEntry *prev_type = child_types[0];
- AstNode *prev_node = child_nodes[0];
- if (prev_type->id == TypeTableEntryIdInvalid) {
- return prev_type;
- }
- for (size_t i = 1; i < child_count; i += 1) {
- TypeTableEntry *cur_type = child_types[i];
- AstNode *cur_node = child_nodes[i];
- if (cur_type->id == TypeTableEntryIdInvalid) {
- return cur_type;
- } else if (types_match_const_cast_only(prev_type, cur_type)) {
- continue;
- } else if (types_match_const_cast_only(cur_type, prev_type)) {
- prev_type = cur_type;
- prev_node = cur_node;
- continue;
- } else if (prev_type->id == TypeTableEntryIdUnreachable) {
- prev_type = cur_type;
- prev_node = cur_node;
- } else if (cur_type->id == TypeTableEntryIdUnreachable) {
- continue;
- } else if (prev_type->id == TypeTableEntryIdInt &&
- cur_type->id == TypeTableEntryIdInt &&
- prev_type->data.integral.is_signed == cur_type->data.integral.is_signed)
- {
- if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) {
- prev_type = cur_type;
- prev_node = cur_node;
- }
- continue;
- } else if (prev_type->id == TypeTableEntryIdFloat &&
- cur_type->id == TypeTableEntryIdFloat)
- {
- if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) {
- prev_type = cur_type;
- prev_node = cur_node;
- }
- } else if (prev_type->id == TypeTableEntryIdErrorUnion &&
- types_match_const_cast_only(prev_type->data.error.child_type, cur_type))
- {
- continue;
- } else if (cur_type->id == TypeTableEntryIdErrorUnion &&
- types_match_const_cast_only(cur_type->data.error.child_type, prev_type))
- {
- prev_type = cur_type;
- prev_node = cur_node;
- continue;
- } else if (prev_type->id == TypeTableEntryIdNumLitInt ||
- prev_type->id == TypeTableEntryIdNumLitFloat)
- {
- if (num_lit_fits_in_other_type(g, prev_node, cur_type)) {
- prev_type = cur_type;
- prev_node = cur_node;
- continue;
- } else {
- return g->builtin_types.entry_invalid;
- }
- } else if (cur_type->id == TypeTableEntryIdNumLitInt ||
- cur_type->id == TypeTableEntryIdNumLitFloat)
- {
- if (num_lit_fits_in_other_type(g, cur_node, prev_type)) {
- continue;
- } else {
- return g->builtin_types.entry_invalid;
- }
- } else {
- add_node_error(g, parent_source_node,
- buf_sprintf("incompatible types: '%s' and '%s'",
- buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
-
- return g->builtin_types.entry_invalid;
- }
- }
- return prev_type;
-}
-
static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_type,
TypeTableEntry *actual_type, AstNode *literal_node, bool *reported_err)
{
@@ -2272,104 +2335,6 @@ static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_
return false;
}
-static AstNode *create_ast_node(CodeGen *g, ImportTableEntry *import, NodeType kind, AstNode *source_node) {
- AstNode *node = allocate<AstNode>(1);
- node->type = kind;
- node->owner = import;
- node->create_index = g->next_node_index;
- g->next_node_index += 1;
- node->line = source_node->line;
- node->column = source_node->column;
- return node;
-}
-
-static AstNode *create_ast_type_node(CodeGen *g, ImportTableEntry *import, TypeTableEntry *type_entry,
- AstNode *source_node)
-{
- AstNode *node = create_ast_node(g, import, NodeTypeSymbol, source_node);
- node->data.symbol_expr.override_type_entry = type_entry;
- return node;
-}
-
-static AstNode *create_ast_void_node(CodeGen *g, ImportTableEntry *import, AstNode *source_node) {
- AstNode *node = create_ast_node(g, import, NodeTypeContainerInitExpr, source_node);
- node->data.container_init_expr.kind = ContainerInitKindArray;
- node->data.container_init_expr.type = create_ast_type_node(g, import, g->builtin_types.entry_void,
- source_node);
- normalize_parent_ptrs(node);
- return node;
-}
-
-static TypeTableEntry *create_and_analyze_cast_node(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, TypeTableEntry *cast_to_type, AstNode *node)
-{
- AstNode *new_parent_node = create_ast_node(g, import, NodeTypeFnCallExpr, node);
- *node->parent_field = new_parent_node;
- new_parent_node->parent_field = node->parent_field;
-
- new_parent_node->data.fn_call_expr.fn_ref_expr = create_ast_type_node(g, import, cast_to_type, node);
- new_parent_node->data.fn_call_expr.params.append(node);
- normalize_parent_ptrs(new_parent_node);
-
- return analyze_expression(g, import, context, cast_to_type, new_parent_node);
-}
-
-static TypeTableEntry *resolve_type_compatibility(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node,
- TypeTableEntry *expected_type, TypeTableEntry *actual_type)
-{
- if (expected_type == nullptr)
- return actual_type; // anything will do
- if (expected_type == actual_type)
- return expected_type; // match
- if (expected_type->id == TypeTableEntryIdInvalid || actual_type->id == TypeTableEntryIdInvalid)
- return g->builtin_types.entry_invalid;
- if (actual_type->id == TypeTableEntryIdUnreachable)
- return actual_type;
-
- bool reported_err = false;
- if (types_match_with_implicit_cast(g, expected_type, actual_type, node, &reported_err)) {
- return create_and_analyze_cast_node(g, import, context, expected_type, node);
- }
-
- if (!reported_err) {
- add_node_error(g, first_executing_node(node),
- buf_sprintf("expected type '%s', got '%s'",
- buf_ptr(&expected_type->name),
- buf_ptr(&actual_type->name)));
- }
-
- return g->builtin_types.entry_invalid;
-}
-
-TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import,
- BlockContext *block_context, AstNode *parent_source_node,
- AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count)
-{
- assert(child_count > 0);
-
- TypeTableEntry *expected_type = determine_peer_type_compatibility(g, parent_source_node,
- child_nodes, child_types, child_count);
-
- if (expected_type->id == TypeTableEntryIdInvalid) {
- return expected_type;
- }
-
- for (size_t i = 0; i < child_count; i += 1) {
- if (!child_nodes[i]) {
- continue;
- }
- AstNode **child_node = child_nodes[i]->parent_field;
- TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, block_context,
- *child_node, expected_type, child_types[i]);
- Expr *expr = get_resolved_expr(*child_node);
- expr->type_entry = resolved_type;
- add_global_const_expr(g, *child_node);
- }
-
- return expected_type;
-}
-
BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
BlockContext *context = allocate<BlockContext>(1);
context->node = node;
@@ -2432,67 +2397,6 @@ TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) {
return nullptr;
}
-static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode *field_access_node, AstNode *value_node, TypeTableEntry *enum_type, Buf *field_name,
- AstNode *out_node)
-{
- assert(field_access_node->type == NodeTypeFieldAccessExpr);
-
- TypeEnumField *type_enum_field = find_enum_type_field(enum_type, field_name);
- if (type_enum_field->type_entry->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- field_access_node->data.field_access_expr.type_enum_field = type_enum_field;
-
- if (type_enum_field) {
- if (value_node) {
- AstNode **value_node_ptr = value_node->parent_field;
- TypeTableEntry *value_type = analyze_expression(g, import, context,
- type_enum_field->type_entry, value_node);
-
- if (value_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- StructValExprCodeGen *codegen = &field_access_node->data.field_access_expr.resolved_struct_val_expr;
- codegen->type_entry = enum_type;
- codegen->source_node = field_access_node;
-
- ConstExprValue *value_const_val = &get_resolved_expr(*value_node_ptr)->const_val;
- if (value_const_val->ok) {
- ConstExprValue *const_val = &get_resolved_expr(out_node)->const_val;
- const_val->ok = true;
- const_val->data.x_enum.tag = type_enum_field->value;
- const_val->data.x_enum.payload = value_const_val;
- } else {
- if (context->fn_entry) {
- context->fn_entry->struct_val_expr_alloca_list.append(codegen);
- } else {
- add_node_error(g, *value_node_ptr, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- }
- }
- } else if (type_enum_field->type_entry->id != TypeTableEntryIdVoid) {
- add_node_error(g, field_access_node,
- buf_sprintf("enum value '%s.%s' requires parameter of type '%s'",
- buf_ptr(&enum_type->name),
- buf_ptr(field_name),
- buf_ptr(&type_enum_field->type_entry->name)));
- } else {
- Expr *expr = get_resolved_expr(out_node);
- expr->const_val.ok = true;
- expr->const_val.data.x_enum.tag = type_enum_field->value;
- expr->const_val.data.x_enum.payload = nullptr;
- }
- } else {
- add_node_error(g, field_access_node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
- buf_ptr(&enum_type->name)));
- }
- return enum_type;
-}
-
TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
assert(type_entry->id == TypeTableEntryIdStruct);
assert(type_entry->data.structure.complete);
@@ -2587,1601 +2491,6 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
}
}
-static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g,
- TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
-{
- assert(node->type == NodeTypeFieldAccessExpr);
- if (!is_slice(bare_struct_type)) {
- BlockContext *container_block_context = get_container_block_context(bare_struct_type);
- assert(container_block_context);
- auto entry = container_block_context->decl_table.maybe_get(field_name);
- AstNode *fn_decl_node = entry ? entry->value : nullptr;
- if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
- resolve_top_level_decl(g, fn_decl_node, false);
- TopLevelDecl *tld = get_as_top_level_decl(fn_decl_node);
- if (tld->resolution == TldResolutionInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- node->data.field_access_expr.is_member_fn = true;
- FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry;
- if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
- return resolve_expr_const_val_as_generic_fn(g, node, fn_entry->type_entry, false);
- } else {
- return resolve_expr_const_val_as_fn(g, node, fn_entry, false);
- }
- }
- }
- add_node_error(g, node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
- return g->builtin_types.entry_invalid;
-}
-
-static TypeTableEntry *analyze_container_member_access(CodeGen *g,
- Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
-{
- TypeTableEntry *bare_type = container_ref_type(struct_type);
- if (!type_is_complete(bare_type)) {
- resolve_container_type(g, bare_type);
- }
-
- node->data.field_access_expr.bare_container_type = bare_type;
-
- if (bare_type->id == TypeTableEntryIdStruct) {
- node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_type, field_name);
- if (node->data.field_access_expr.type_struct_field) {
- return node->data.field_access_expr.type_struct_field->type_entry;
- } else {
- return analyze_container_member_access_inner(g, bare_type, field_name,
- node, struct_type);
- }
- } else if (bare_type->id == TypeTableEntryIdEnum) {
- node->data.field_access_expr.type_enum_field = find_enum_type_field(bare_type, field_name);
- if (node->data.field_access_expr.type_enum_field) {
- return node->data.field_access_expr.type_enum_field->type_entry;
- } else {
- return analyze_container_member_access_inner(g, bare_type, field_name,
- node, struct_type);
- }
- } else if (bare_type->id == TypeTableEntryIdUnion) {
- zig_panic("TODO");
- } else {
- zig_unreachable();
- }
-}
-
-static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeFieldAccessExpr);
-
- AstNode **struct_expr_node = &node->data.field_access_expr.struct_expr;
- TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node);
- Buf *field_name = node->data.field_access_expr.field_name;
-
- if (struct_type->id == TypeTableEntryIdInvalid) {
- return struct_type;
- } else if (is_container_ref(struct_type)) {
- return analyze_container_member_access(g, field_name, node, struct_type);
- } else if (struct_type->id == TypeTableEntryIdArray) {
- if (buf_eql_str(field_name, "len")) {
- return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
- struct_type->data.array.len, false);
- } else {
- add_node_error(g, node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
- buf_ptr(&struct_type->name)));
- return g->builtin_types.entry_invalid;
- }
- } else if (struct_type->id == TypeTableEntryIdMetaType) {
- TypeTableEntry *child_type = resolve_type(g, *struct_expr_node);
-
- if (child_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (child_type->id == TypeTableEntryIdEnum) {
- AstNode *container_init_node = node->data.field_access_expr.container_init_expr_node;
- AstNode *value_node;
- if (container_init_node) {
- assert(container_init_node->type == NodeTypeContainerInitExpr);
- size_t param_count = container_init_node->data.container_init_expr.entries.length;
- if (param_count > 1) {
- AstNode *first_invalid_node = container_init_node->data.container_init_expr.entries.at(1);
- add_node_error(g, first_executing_node(first_invalid_node),
- buf_sprintf("enum values accept only one parameter"));
- return child_type;
- } else {
- if (param_count == 1) {
- value_node = container_init_node->data.container_init_expr.entries.at(0);
- } else {
- value_node = nullptr;
- }
- container_init_node->data.container_init_expr.enum_type = child_type;
- }
- } else {
- value_node = nullptr;
- }
- return analyze_enum_value_expr(g, import, context, node, value_node, child_type, field_name, node);
- } else if (child_type->id == TypeTableEntryIdStruct) {
- BlockContext *container_block_context = get_container_block_context(child_type);
- auto entry = container_block_context->decl_table.maybe_get(field_name);
- AstNode *decl_node = entry ? entry->value : nullptr;
- if (decl_node) {
- bool pointer_only = false;
- return analyze_decl_ref(g, node, decl_node, pointer_only, context, false);
- } else {
- add_node_error(g, node,
- buf_sprintf("container '%s' has no member called '%s'",
- buf_ptr(&child_type->name), buf_ptr(field_name)));
- return g->builtin_types.entry_invalid;
- }
- } else if (child_type->id == TypeTableEntryIdPureError) {
- return analyze_error_literal_expr(g, import, context, node, field_name);
- } else if (child_type->id == TypeTableEntryIdInt) {
- bool depends_on_compile_var =
- get_resolved_expr(*struct_expr_node)->const_val.depends_on_compile_var;
- if (buf_eql_str(field_name, "bit_count")) {
- return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
- child_type->data.integral.bit_count, depends_on_compile_var);
- } else if (buf_eql_str(field_name, "is_signed")) {
- return resolve_expr_const_val_as_bool(g, node, child_type->data.integral.is_signed,
- depends_on_compile_var);
- } else {
- add_node_error(g, node,
- buf_sprintf("type '%s' has no member called '%s'",
- buf_ptr(&child_type->name), buf_ptr(field_name)));
- return g->builtin_types.entry_invalid;
- }
- } else {
- add_node_error(g, node,
- buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
- return g->builtin_types.entry_invalid;
- }
- } else if (struct_type->id == TypeTableEntryIdNamespace) {
- ConstExprValue *const_val = &get_resolved_expr(*struct_expr_node)->const_val;
- assert(const_val->ok);
- ImportTableEntry *namespace_import = const_val->data.x_import;
- AstNode *decl_node = find_decl(namespace_import->block_context, field_name);
- if (!decl_node) {
- // we must now resolve all the use decls
- for (size_t i = 0; i < namespace_import->use_decls.length; i += 1) {
- AstNode *use_decl_node = namespace_import->use_decls.at(i);
- if (!get_resolved_expr(use_decl_node->data.use.expr)->type_entry) {
- preview_use_decl(g, use_decl_node);
- }
- resolve_use_decl(g, use_decl_node);
- }
- decl_node = find_decl(namespace_import->block_context, field_name);
- }
- if (decl_node) {
- TopLevelDecl *tld = get_as_top_level_decl(decl_node);
- if (tld->visib_mod == VisibModPrivate && decl_node->owner != import) {
- ErrorMsg *msg = add_node_error(g, node,
- buf_sprintf("'%s' is private", buf_ptr(field_name)));
- add_error_note(g, msg, decl_node, buf_sprintf("declared here"));
- }
- bool pointer_only = false;
- return analyze_decl_ref(g, node, decl_node, pointer_only, context,
- const_val->depends_on_compile_var);
- } else {
- const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)";
- add_node_error(g, node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name));
- return g->builtin_types.entry_invalid;
- }
- } else {
- add_node_error(g, node,
- buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
- return g->builtin_types.entry_invalid;
- }
-}
-
-static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode *node)
-{
- assert(node->type == NodeTypeSliceExpr);
-
- TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr,
- node->data.slice_expr.array_ref_expr);
-
- TypeTableEntry *return_type;
-
- if (array_type->id == TypeTableEntryIdInvalid) {
- return_type = g->builtin_types.entry_invalid;
- } else if (array_type->id == TypeTableEntryIdArray) {
- return_type = get_slice_type(g, array_type->data.array.child_type,
- node->data.slice_expr.is_const);
- } else if (array_type->id == TypeTableEntryIdPointer) {
- return_type = get_slice_type(g, array_type->data.pointer.child_type,
- node->data.slice_expr.is_const);
- } else if (array_type->id == TypeTableEntryIdStruct &&
- array_type->data.structure.is_slice)
- {
- return_type = get_slice_type(g,
- array_type->data.structure.fields[0].type_entry->data.pointer.child_type,
- node->data.slice_expr.is_const);
- } else {
- add_node_error(g, node,
- buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
- return_type = g->builtin_types.entry_invalid;
- }
-
- if (return_type->id != TypeTableEntryIdInvalid) {
- node->data.slice_expr.resolved_struct_val_expr.type_entry = return_type;
- node->data.slice_expr.resolved_struct_val_expr.source_node = node;
- context->fn_entry->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr);
- }
-
- analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.start);
-
- if (node->data.slice_expr.end) {
- analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.end);
- }
-
- return return_type;
-}
-
-static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode *node, LValPurpose purpose)
-{
- TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr,
- node->data.array_access_expr.array_ref_expr);
-
- TypeTableEntry *return_type;
-
- if (array_type->id == TypeTableEntryIdInvalid) {
- return_type = g->builtin_types.entry_invalid;
- } else if (array_type->id == TypeTableEntryIdArray) {
- if (array_type->data.array.len == 0) {
- add_node_error(g, node, buf_sprintf("out of bounds array access"));
- }
- return_type = array_type->data.array.child_type;
- } else if (array_type->id == TypeTableEntryIdPointer) {
- if (array_type->data.pointer.is_const && purpose == LValPurposeAssign) {
- add_node_error(g, node, buf_sprintf("cannot assign to constant"));
- return g->builtin_types.entry_invalid;
- }
- return_type = array_type->data.pointer.child_type;
- } else if (array_type->id == TypeTableEntryIdStruct &&
- array_type->data.structure.is_slice)
- {
- TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry;
- if (pointer_type->data.pointer.is_const && purpose == LValPurposeAssign) {
- add_node_error(g, node, buf_sprintf("cannot assign to constant"));
- return g->builtin_types.entry_invalid;
- }
- return_type = pointer_type->data.pointer.child_type;
- } else {
- add_node_error(g, node,
- buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
- return_type = g->builtin_types.entry_invalid;
- }
-
- analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.array_access_expr.subscript);
-
- return return_type;
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type,
- bool depends_on_compile_var)
-{
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.data.x_type = type;
- expr->const_val.depends_on_compile_var = depends_on_compile_var;
- return g->builtin_types.entry_type;
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode *node, AstNode *other,
- bool depends_on_compile_var)
-{
- Expr *expr = get_resolved_expr(node);
- Expr *other_expr = get_resolved_expr(other);
- expr->const_val = other_expr->const_val;
- expr->const_val.depends_on_compile_var = expr->const_val.depends_on_compile_var ||
- depends_on_compile_var;
- return other_expr->type_entry;
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn,
- bool depends_on_compile_var)
-{
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.data.x_fn = fn;
- expr->const_val.depends_on_compile_var = depends_on_compile_var;
- return fn->type_entry;
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node,
- TypeTableEntry *type_entry, bool depends_on_compile_var)
-{
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.data.x_type = type_entry;
- expr->const_val.depends_on_compile_var = depends_on_compile_var;
- return type_entry;
-}
-
-
-static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, ErrorTableEntry *err) {
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.data.x_err.err = err;
- return g->builtin_types.entry_pure_error;
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value,
- bool depends_on_compile_var)
-{
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.depends_on_compile_var = depends_on_compile_var;
- expr->const_val.data.x_bool = value;
- return g->builtin_types.entry_bool;
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNode *node, Buf *str) {
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
-
- size_t len_with_null = buf_len(str) + 1;
- expr->const_val.data.x_ptr.ptr = allocate<ConstExprValue*>(len_with_null);
- expr->const_val.data.x_ptr.len = len_with_null;
- expr->const_val.data.x_ptr.is_c_str = true;
-
- ConstExprValue *all_chars = allocate<ConstExprValue>(len_with_null);
- for (size_t i = 0; i < buf_len(str); i += 1) {
- ConstExprValue *this_char = &all_chars[i];
- this_char->ok = true;
- bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]);
- expr->const_val.data.x_ptr.ptr[i] = this_char;
- }
-
- ConstExprValue *null_char = &all_chars[len_with_null - 1];
- null_char->ok = true;
- bignum_init_unsigned(&null_char->data.x_bignum, 0);
- expr->const_val.data.x_ptr.ptr[len_with_null - 1] = null_char;
-
- return get_pointer_to_type(g, g->builtin_types.entry_u8, true);
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_string_lit(CodeGen *g, AstNode *node, Buf *str) {
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.data.x_array.fields = allocate<ConstExprValue*>(buf_len(str));
-
- ConstExprValue *all_chars = allocate<ConstExprValue>(buf_len(str));
- for (size_t i = 0; i < buf_len(str); i += 1) {
- ConstExprValue *this_char = &all_chars[i];
- this_char->ok = true;
- bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]);
- expr->const_val.data.x_array.fields[i] = this_char;
- }
- return get_array_type(g, g->builtin_types.entry_u8, buf_len(str));
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_bignum(CodeGen *g, AstNode *node,
- TypeTableEntry *expected_type, BigNum *bignum, bool depends_on_compile_var)
-{
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.depends_on_compile_var = depends_on_compile_var;
-
- bignum_init_bignum(&expr->const_val.data.x_bignum, bignum);
- if (bignum->kind == BigNumKindInt) {
- return g->builtin_types.entry_num_lit_int;
- } else if (bignum->kind == BigNumKindFloat) {
- return g->builtin_types.entry_num_lit_float;
- } else {
- zig_unreachable();
- }
-}
-
-static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
- TypeTableEntry *expected_type, uint64_t x, bool depends_on_compile_var)
-{
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.depends_on_compile_var = depends_on_compile_var;
-
- bignum_init_unsigned(&expr->const_val.data.x_bignum, x);
-
- return g->builtin_types.entry_num_lit_int;
-}
-
-static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node, Buf *err_name)
-{
- auto err_table_entry = g->error_table.maybe_get(err_name);
-
- if (err_table_entry) {
- return resolve_expr_const_val_as_err(g, node, err_table_entry->value);
- }
-
- add_node_error(g, node,
- buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name)));
-
- return g->builtin_types.entry_invalid;
-}
-
-static bool var_is_pure(VariableTableEntry *var, BlockContext *context) {
- if (var->block_context->fn_entry == context->fn_entry) {
- // variable was declared in the current function, so it's OK.
- return true;
- }
- return var->src_is_const && var->type->deep_const;
-}
-
-static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var,
- BlockContext *context, bool depends_on_compile_var)
-{
- get_resolved_expr(source_node)->variable = var;
- if (!var_is_pure(var, context)) {
- mark_impure_fn(g, context, source_node);
- }
- if (var->src_is_const && var->val_node) {
- ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val;
- if (other_const_val->ok) {
- return resolve_expr_const_val_as_other_expr(g, source_node, var->val_node,
- depends_on_compile_var || var->force_depends_on_compile_var);
- }
- }
- return var->type;
-}
-
-static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
- bool pointer_only, BlockContext *block_context, bool depends_on_compile_var)
-{
- resolve_top_level_decl(g, decl_node, pointer_only);
- TopLevelDecl *tld = get_as_top_level_decl(decl_node);
- if (tld->resolution == TldResolutionInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- if (decl_node->type == NodeTypeVariableDeclaration) {
- VariableTableEntry *var = decl_node->data.variable_declaration.variable;
- return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var);
- } else if (decl_node->type == NodeTypeFnProto) {
- FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
- assert(fn_entry->type_entry);
- if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
- return resolve_expr_const_val_as_generic_fn(g, source_node, fn_entry->type_entry, depends_on_compile_var);
- } else {
- return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var);
- }
- } else if (decl_node->type == NodeTypeContainerDecl) {
- if (decl_node->data.struct_decl.generic_params.length > 0) {
- TypeTableEntry *type_entry = decl_node->data.struct_decl.generic_fn_type;
- assert(type_entry);
- return resolve_expr_const_val_as_generic_fn(g, source_node, type_entry, depends_on_compile_var);
- } else {
- return resolve_expr_const_val_as_type(g, source_node, decl_node->data.struct_decl.type_entry,
- depends_on_compile_var);
- }
- } else if (decl_node->type == NodeTypeTypeDecl) {
- return resolve_expr_const_val_as_type(g, source_node, decl_node->data.type_decl.child_type_entry,
- depends_on_compile_var);
- } else {
- zig_unreachable();
- }
-}
-
-static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
-{
- Buf *variable_name = node->data.symbol_expr.symbol;
-
- auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name);
- if (primitive_table_entry) {
- return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value, false);
- }
-
- VariableTableEntry *var = find_variable(g, context, variable_name);
- if (var) {
- TypeTableEntry *var_type = analyze_var_ref(g, node, var, context, false);
- return var_type;
- }
-
- AstNode *decl_node = find_decl(context, variable_name);
- if (decl_node) {
- return analyze_decl_ref(g, node, decl_node, pointer_only, context, false);
- }
-
- if (import->any_imports_failed) {
- // skip the error message since we had a failing import in this file
- // if an import breaks we don't need 9999 undeclared identifier errors
- return g->builtin_types.entry_invalid;
- }
-
- mark_impure_fn(g, context, node);
- add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
- return g->builtin_types.entry_invalid;
-}
-
-static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
- switch (op) {
- case BinOpTypeAssign:
- return true;
- case BinOpTypeAssignTimes:
- case BinOpTypeAssignTimesWrap:
- case BinOpTypeAssignDiv:
- case BinOpTypeAssignMod:
- return type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdFloat;
- case BinOpTypeAssignPlus:
- case BinOpTypeAssignPlusWrap:
- case BinOpTypeAssignMinus:
- case BinOpTypeAssignMinusWrap:
- return type->id == TypeTableEntryIdInt ||
- type->id == TypeTableEntryIdFloat ||
- type->id == TypeTableEntryIdPointer;
- case BinOpTypeAssignBitShiftLeft:
- case BinOpTypeAssignBitShiftLeftWrap:
- case BinOpTypeAssignBitShiftRight:
- case BinOpTypeAssignBitAnd:
- case BinOpTypeAssignBitXor:
- case BinOpTypeAssignBitOr:
- return type->id == TypeTableEntryIdInt;
- case BinOpTypeAssignBoolAnd:
- case BinOpTypeAssignBoolOr:
- return type->id == TypeTableEntryIdBool;
-
- case BinOpTypeInvalid:
- case BinOpTypeBoolOr:
- case BinOpTypeBoolAnd:
- case BinOpTypeCmpEq:
- case BinOpTypeCmpNotEq:
- case BinOpTypeCmpLessThan:
- case BinOpTypeCmpGreaterThan:
- case BinOpTypeCmpLessOrEq:
- case BinOpTypeCmpGreaterOrEq:
- case BinOpTypeBinOr:
- case BinOpTypeBinXor:
- case BinOpTypeBinAnd:
- case BinOpTypeBitShiftLeft:
- case BinOpTypeBitShiftLeftWrap:
- case BinOpTypeBitShiftRight:
- case BinOpTypeAdd:
- case BinOpTypeAddWrap:
- case BinOpTypeSub:
- case BinOpTypeSubWrap:
- case BinOpTypeMult:
- case BinOpTypeMultWrap:
- case BinOpTypeDiv:
- case BinOpTypeMod:
- case BinOpTypeUnwrapMaybe:
- case BinOpTypeArrayCat:
- case BinOpTypeArrayMult:
- zig_unreachable();
- }
- zig_unreachable();
-}
-
-static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
- AstNode *lhs_node, LValPurpose purpose, bool is_ptr_const)
-{
- TypeTableEntry *expected_rhs_type = nullptr;
- lhs_node->block_context = block_context;
- if (lhs_node->type == NodeTypeSymbol) {
- bool pointer_only = purpose == LValPurposeAddressOf;
- expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node, pointer_only);
- if (expected_rhs_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
- if (purpose != LValPurposeAddressOf) {
- Buf *name = lhs_node->data.symbol_expr.symbol;
- VariableTableEntry *var = find_variable(g, block_context, name);
- if (var) {
- if (var->src_is_const) {
- add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant"));
- expected_rhs_type = g->builtin_types.entry_invalid;
- } else {
- expected_rhs_type = var->type;
- get_resolved_expr(lhs_node)->variable = var;
- }
- } else {
- add_node_error(g, lhs_node,
- buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
- expected_rhs_type = g->builtin_types.entry_invalid;
- }
- }
- } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
- expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node, purpose);
- } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
- expected_rhs_type = analyze_field_access_expr(g, import, block_context, nullptr, lhs_node);
- } else if (lhs_node->type == NodeTypePrefixOpExpr &&
- lhs_node->data.prefix_op_expr.prefix_op == PrefixOpDereference)
- {
- assert(purpose == LValPurposeAssign);
- AstNode *target_node = lhs_node->data.prefix_op_expr.primary_expr;
- TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, target_node);
- if (type_entry->id == TypeTableEntryIdInvalid) {
- expected_rhs_type = type_entry;
- } else if (type_entry->id == TypeTableEntryIdPointer) {
- expected_rhs_type = type_entry->data.pointer.child_type;
- } else {
- add_node_error(g, target_node,
- buf_sprintf("indirection requires pointer operand ('%s' invalid)",
- buf_ptr(&type_entry->name)));
- expected_rhs_type = g->builtin_types.entry_invalid;
- }
- } else {
- if (purpose == LValPurposeAssign) {
- add_node_error(g, lhs_node, buf_sprintf("invalid assignment target"));
- expected_rhs_type = g->builtin_types.entry_invalid;
- } else if (purpose == LValPurposeAddressOf) {
- TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, lhs_node);
- if (type_entry->id == TypeTableEntryIdInvalid) {
- expected_rhs_type = g->builtin_types.entry_invalid;
- } else if (type_entry->id == TypeTableEntryIdMetaType) {
- expected_rhs_type = type_entry;
- } else {
- add_node_error(g, lhs_node, buf_sprintf("invalid addressof target"));
- expected_rhs_type = g->builtin_types.entry_invalid;
- }
- }
- }
- assert(expected_rhs_type);
- return expected_rhs_type;
-}
-
-static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode *node)
-{
- assert(node->type == NodeTypeBinOpExpr);
- BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
-
- AstNode **op1 = &node->data.bin_op_expr.op1;
- AstNode **op2 = &node->data.bin_op_expr.op2;
- TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
- TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
-
- AstNode *op_nodes[] = {*op1, *op2};
- TypeTableEntry *op_types[] = {op1_type, op2_type};
-
- TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
- op_nodes, op_types, 2);
-
- bool is_equality_cmp = (bin_op_type == BinOpTypeCmpEq || bin_op_type == BinOpTypeCmpNotEq);
-
- switch (resolved_type->id) {
- case TypeTableEntryIdInvalid:
- return g->builtin_types.entry_invalid;
-
- case TypeTableEntryIdNumLitFloat:
- case TypeTableEntryIdNumLitInt:
- case TypeTableEntryIdInt:
- case TypeTableEntryIdFloat:
- break;
-
- case TypeTableEntryIdBool:
- case TypeTableEntryIdMetaType:
- case TypeTableEntryIdVoid:
- case TypeTableEntryIdPointer:
- case TypeTableEntryIdPureError:
- case TypeTableEntryIdFn:
- case TypeTableEntryIdTypeDecl:
- case TypeTableEntryIdNamespace:
- case TypeTableEntryIdBlock:
- case TypeTableEntryIdGenericFn:
- if (!is_equality_cmp) {
- add_node_error(g, node,
- buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
- return g->builtin_types.entry_invalid;
- }
- break;
-
- case TypeTableEntryIdEnum:
- if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) {
- add_node_error(g, node,
- buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
- return g->builtin_types.entry_invalid;
- }
- break;
-
- case TypeTableEntryIdUnreachable:
- case TypeTableEntryIdArray:
- case TypeTableEntryIdStruct:
- case TypeTableEntryIdUndefLit:
- case TypeTableEntryIdNullLit:
- case TypeTableEntryIdMaybe:
- case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdUnion:
- add_node_error(g, node,
- buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
- return g->builtin_types.entry_invalid;
-
- case TypeTableEntryIdVar:
- zig_unreachable();
- }
-
- ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
- ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
- if (!op1_val->ok || !op2_val->ok) {
- return g->builtin_types.entry_bool;
- }
-
-
- ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
- eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val);
- return g->builtin_types.entry_bool;
-
-}
-
-static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode *node)
-{
- assert(node->type == NodeTypeBinOpExpr);
- BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
-
- AstNode *op1 = node->data.bin_op_expr.op1;
- AstNode *op2 = node->data.bin_op_expr.op2;
- TypeTableEntry *op1_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op1);
- TypeTableEntry *op2_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op2);
-
- if (op1_type->id == TypeTableEntryIdInvalid ||
- op2_type->id == TypeTableEntryIdInvalid)
- {
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
- ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
- if (!op1_val->ok || !op2_val->ok) {
- return g->builtin_types.entry_bool;
- }
-
- ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
- eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val);
- return g->builtin_types.entry_bool;
-}
-
-static TypeTableEntry *analyze_array_mult(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeBinOpExpr);
- assert(node->data.bin_op_expr.bin_op == BinOpTypeArrayMult);
-
- AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
- AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
-
- TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
- TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
-
- if (op1_type->id == TypeTableEntryIdInvalid ||
- op2_type->id == TypeTableEntryIdInvalid)
- {
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
- ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
-
- AstNode *bad_node;
- if (!op1_val->ok) {
- bad_node = *op1;
- } else if (!op2_val->ok) {
- bad_node = *op2;
- } else {
- bad_node = nullptr;
- }
- if (bad_node) {
- add_node_error(g, bad_node, buf_sprintf("array multiplication requires constant expression"));
- return g->builtin_types.entry_invalid;
- }
-
- if (op1_type->id != TypeTableEntryIdArray) {
- add_node_error(g, *op1,
- buf_sprintf("expected array type, got '%s'", buf_ptr(&op1_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- if (op2_type->id != TypeTableEntryIdNumLitInt &&
- op2_type->id != TypeTableEntryIdInt)
- {
- add_node_error(g, *op2, buf_sprintf("expected integer type, got '%s'", buf_ptr(&op2_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- if (op2_val->data.x_bignum.is_negative) {
- add_node_error(g, *op2, buf_sprintf("expected positive number"));
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
- const_val->ok = true;
- const_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
-
- TypeTableEntry *child_type = op1_type->data.array.child_type;
- BigNum old_array_len;
- bignum_init_unsigned(&old_array_len, op1_type->data.array.len);
-
- BigNum new_array_len;
- if (bignum_mul(&new_array_len, &old_array_len, &op2_val->data.x_bignum)) {
- add_node_error(g, node, buf_sprintf("operation results in overflow"));
- return g->builtin_types.entry_invalid;
- }
-
- uint64_t old_array_len_bare = op1_type->data.array.len;
- uint64_t operand_amt = op2_val->data.x_bignum.data.x_uint;
-
- uint64_t new_array_len_bare = new_array_len.data.x_uint;
- const_val->data.x_array.fields = allocate<ConstExprValue*>(new_array_len_bare);
-
- uint64_t i = 0;
- for (uint64_t x = 0; x < operand_amt; x += 1) {
- for (uint64_t y = 0; y < old_array_len_bare; y += 1) {
- const_val->data.x_array.fields[i] = op1_val->data.x_array.fields[y];
- i += 1;
- }
- }
-
- return get_array_type(g, child_type, new_array_len_bare);
-}
-
-static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeBinOpExpr);
- BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
- switch (bin_op_type) {
- case BinOpTypeAssign:
- case BinOpTypeAssignTimes:
- case BinOpTypeAssignTimesWrap:
- case BinOpTypeAssignDiv:
- case BinOpTypeAssignMod:
- case BinOpTypeAssignPlus:
- case BinOpTypeAssignPlusWrap:
- case BinOpTypeAssignMinus:
- case BinOpTypeAssignMinusWrap:
- case BinOpTypeAssignBitShiftLeft:
- case BinOpTypeAssignBitShiftLeftWrap:
- case BinOpTypeAssignBitShiftRight:
- case BinOpTypeAssignBitAnd:
- case BinOpTypeAssignBitXor:
- case BinOpTypeAssignBitOr:
- case BinOpTypeAssignBoolAnd:
- case BinOpTypeAssignBoolOr:
- {
- AstNode *lhs_node = node->data.bin_op_expr.op1;
-
- TypeTableEntry *expected_rhs_type = analyze_lvalue(g, import, context, lhs_node,
- LValPurposeAssign, false);
- if (expected_rhs_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (!is_op_allowed(expected_rhs_type, node->data.bin_op_expr.bin_op)) {
- if (expected_rhs_type->id != TypeTableEntryIdInvalid) {
- add_node_error(g, lhs_node,
- buf_sprintf("operator not allowed for type '%s'",
- buf_ptr(&expected_rhs_type->name)));
- }
- }
-
- analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
- // not const ok because expression has side effects
- return g->builtin_types.entry_void;
- }
- case BinOpTypeBoolOr:
- case BinOpTypeBoolAnd:
- return analyze_logic_bin_op_expr(g, import, context, node);
- case BinOpTypeCmpEq:
- case BinOpTypeCmpNotEq:
- case BinOpTypeCmpLessThan:
- case BinOpTypeCmpGreaterThan:
- case BinOpTypeCmpLessOrEq:
- case BinOpTypeCmpGreaterOrEq:
- return analyze_bool_bin_op_expr(g, import, context, node);
- case BinOpTypeBinOr:
- case BinOpTypeBinXor:
- case BinOpTypeBinAnd:
- case BinOpTypeBitShiftLeft:
- case BinOpTypeBitShiftLeftWrap:
- case BinOpTypeBitShiftRight:
- case BinOpTypeAdd:
- case BinOpTypeAddWrap:
- case BinOpTypeSub:
- case BinOpTypeSubWrap:
- case BinOpTypeMult:
- case BinOpTypeMultWrap:
- case BinOpTypeDiv:
- case BinOpTypeMod:
- {
- AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
- AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
- TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, *op1);
- TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, *op2);
-
- AstNode *op_nodes[] = {*op1, *op2};
- TypeTableEntry *op_types[] = {lhs_type, rhs_type};
-
- TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
- op_nodes, op_types, 2);
-
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- }
-
- if (resolved_type->id == TypeTableEntryIdInt ||
- resolved_type->id == TypeTableEntryIdNumLitInt)
- {
- // int
- } else if ((resolved_type->id == TypeTableEntryIdFloat ||
- resolved_type->id == TypeTableEntryIdNumLitFloat) &&
- (bin_op_type == BinOpTypeAdd ||
- bin_op_type == BinOpTypeSub ||
- bin_op_type == BinOpTypeMult ||
- bin_op_type == BinOpTypeDiv ||
- bin_op_type == BinOpTypeMod))
- {
- // float
- } else {
- add_node_error(g, node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
- buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
- ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
- if (!op1_val->ok || !op2_val->ok) {
- return resolved_type;
- }
-
- ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
- int err;
- if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type,
- op2_val, resolved_type, out_val)))
- {
- if (err == ErrorDivByZero) {
- add_node_error(g, node, buf_sprintf("division by zero is undefined"));
- return g->builtin_types.entry_invalid;
- } else if (err == ErrorOverflow) {
- add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type"));
- return g->builtin_types.entry_invalid;
- }
- return g->builtin_types.entry_invalid;
- }
-
- num_lit_fits_in_other_type(g, node, resolved_type);
- return resolved_type;
- }
- case BinOpTypeUnwrapMaybe:
- {
- AstNode *op1 = node->data.bin_op_expr.op1;
- AstNode *op2 = node->data.bin_op_expr.op2;
- TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
-
- if (lhs_type->id == TypeTableEntryIdInvalid) {
- return lhs_type;
- } else if (lhs_type->id == TypeTableEntryIdMaybe) {
- TypeTableEntry *child_type = lhs_type->data.maybe.child_type;
- analyze_expression(g, import, context, child_type, op2);
- return child_type;
- } else {
- add_node_error(g, op1,
- buf_sprintf("expected maybe type, got '%s'",
- buf_ptr(&lhs_type->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case BinOpTypeArrayCat:
- {
- AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
- AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
-
- TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
- TypeTableEntry *child_type;
- if (op1_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (op1_type->id == TypeTableEntryIdArray) {
- child_type = op1_type->data.array.child_type;
- } else if (op1_type->id == TypeTableEntryIdPointer &&
- op1_type->data.pointer.child_type == g->builtin_types.entry_u8) {
- child_type = op1_type->data.pointer.child_type;
- } else {
- add_node_error(g, *op1, buf_sprintf("expected array or C string literal, got '%s'",
- buf_ptr(&op1_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
-
- if (op2_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (op2_type->id == TypeTableEntryIdArray) {
- if (op2_type->data.array.child_type != child_type) {
- add_node_error(g, *op2, buf_sprintf("expected array of type '%s', got '%s'",
- buf_ptr(&child_type->name),
- buf_ptr(&op2_type->name)));
- return g->builtin_types.entry_invalid;
- }
- } else if (op2_type->id == TypeTableEntryIdPointer &&
- op2_type->data.pointer.child_type == g->builtin_types.entry_u8) {
- } else {
- add_node_error(g, *op2, buf_sprintf("expected array or C string literal, got '%s'",
- buf_ptr(&op2_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
- ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
-
- AstNode *bad_node;
- if (!op1_val->ok) {
- bad_node = *op1;
- } else if (!op2_val->ok) {
- bad_node = *op2;
- } else {
- bad_node = nullptr;
- }
- if (bad_node) {
- add_node_error(g, bad_node, buf_sprintf("array concatenation requires constant expression"));
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
- const_val->ok = true;
- const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
- op2_val->depends_on_compile_var;
-
- if (op1_type->id == TypeTableEntryIdArray) {
- uint64_t new_len = op1_type->data.array.len + op2_type->data.array.len;
- const_val->data.x_array.fields = allocate<ConstExprValue*>(new_len);
- uint64_t next_index = 0;
- for (uint64_t i = 0; i < op1_type->data.array.len; i += 1, next_index += 1) {
- const_val->data.x_array.fields[next_index] = op1_val->data.x_array.fields[i];
- }
- for (uint64_t i = 0; i < op2_type->data.array.len; i += 1, next_index += 1) {
- const_val->data.x_array.fields[next_index] = op2_val->data.x_array.fields[i];
- }
- return get_array_type(g, child_type, new_len);
- } else if (op1_type->id == TypeTableEntryIdPointer) {
- if (!op1_val->data.x_ptr.is_c_str) {
- add_node_error(g, *op1,
- buf_sprintf("expected array or C string literal, got '%s'",
- buf_ptr(&op1_type->name)));
- return g->builtin_types.entry_invalid;
- } else if (!op2_val->data.x_ptr.is_c_str) {
- add_node_error(g, *op2,
- buf_sprintf("expected array or C string literal, got '%s'",
- buf_ptr(&op2_type->name)));
- return g->builtin_types.entry_invalid;
- }
- const_val->data.x_ptr.is_c_str = true;
- const_val->data.x_ptr.len = op1_val->data.x_ptr.len + op2_val->data.x_ptr.len - 1;
- const_val->data.x_ptr.ptr = allocate<ConstExprValue*>(const_val->data.x_ptr.len);
- uint64_t next_index = 0;
- for (uint64_t i = 0; i < op1_val->data.x_ptr.len - 1; i += 1, next_index += 1) {
- const_val->data.x_ptr.ptr[next_index] = op1_val->data.x_ptr.ptr[i];
- }
- for (uint64_t i = 0; i < op2_val->data.x_ptr.len; i += 1, next_index += 1) {
- const_val->data.x_ptr.ptr[next_index] = op2_val->data.x_ptr.ptr[i];
- }
- return op1_type;
- } else {
- zig_unreachable();
- }
- }
- case BinOpTypeArrayMult:
- return analyze_array_mult(g, import, context, expected_type, node);
- case BinOpTypeInvalid:
- zig_unreachable();
- }
- zig_unreachable();
-}
-
-// Set name to nullptr to make the variable anonymous (not visible to programmer).
-// TODO merge with definition of add_local_var in ir.cpp
-static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
- BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node,
- bool shadowable)
-{
- VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
- variable_entry->type = type_entry;
- variable_entry->block_context = context;
- variable_entry->import = import;
- variable_entry->shadowable = shadowable;
- variable_entry->mem_slot_index = SIZE_MAX;
-
- if (name) {
- buf_init_from_buf(&variable_entry->name, name);
-
- if (type_entry->id != TypeTableEntryIdInvalid) {
- VariableTableEntry *existing_var = find_variable(g, context, name);
- if (existing_var && !existing_var->shadowable) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
- add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
- variable_entry->type = g->builtin_types.entry_invalid;
- } else {
- auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
- if (primitive_table_entry) {
- TypeTableEntry *type = primitive_table_entry->value;
- add_node_error(g, source_node,
- buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
- variable_entry->type = g->builtin_types.entry_invalid;
- } else {
- AstNode *decl_node = find_decl(context, name);
- if (decl_node && decl_node->type != NodeTypeVariableDeclaration) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redefinition of '%s'", buf_ptr(name)));
- add_error_note(g, msg, decl_node, buf_sprintf("previous definition is here"));
- variable_entry->type = g->builtin_types.entry_invalid;
- }
- }
- }
- }
-
- context->var_table.put(&variable_entry->name, variable_entry);
- } else {
- // TODO replace _anon with @anon and make sure all tests still pass
- buf_init_from_str(&variable_entry->name, "_anon");
- }
- if (context->fn_entry) {
- context->fn_entry->variable_list.append(variable_entry);
- }
-
- variable_entry->src_is_const = is_const;
- variable_entry->gen_is_const = is_const;
- variable_entry->decl_node = source_node;
- variable_entry->val_node = val_node;
-
-
- return variable_entry;
-}
-
-static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
- BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node)
-{
- return add_local_var_shadowable(g, source_node, import, context, name, type_entry, is_const, val_node, false);
-}
-
-static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import,
- BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node)
-{
- AstNode *op1 = node->data.unwrap_err_expr.op1;
- AstNode *op2 = node->data.unwrap_err_expr.op2;
- AstNode *var_node = node->data.unwrap_err_expr.symbol;
-
- TypeTableEntry *lhs_type = analyze_expression(g, import, parent_context, nullptr, op1);
- if (lhs_type->id == TypeTableEntryIdInvalid) {
- return lhs_type;
- } else if (lhs_type->id == TypeTableEntryIdErrorUnion) {
- TypeTableEntry *child_type = lhs_type->data.error.child_type;
- BlockContext *child_context;
- if (var_node) {
- child_context = new_block_context(node, parent_context);
- var_node->block_context = child_context;
- Buf *var_name = var_node->data.symbol_expr.symbol;
- node->data.unwrap_err_expr.var = add_local_var(g, var_node, import, child_context, var_name,
- g->builtin_types.entry_pure_error, true, nullptr);
- } else {
- child_context = parent_context;
- }
-
- analyze_expression(g, import, child_context, child_type, op2);
- return child_type;
- } else {
- add_node_error(g, op1,
- buf_sprintf("expected error type, got '%s'", buf_ptr(&lhs_type->name)));
- return g->builtin_types.entry_invalid;
- }
-}
-
-
-static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *source_node,
- AstNodeVariableDeclaration *variable_declaration,
- bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr)
-{
- bool is_const = variable_declaration->is_const;
- bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport);
- bool is_extern = variable_declaration->is_extern;
-
- TypeTableEntry *explicit_type = nullptr;
- if (variable_declaration->type != nullptr) {
- explicit_type = analyze_type_expr(g, import, context, variable_declaration->type);
- if (explicit_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, variable_declaration->type,
- buf_sprintf("variable of type 'unreachable' not allowed"));
- explicit_type = g->builtin_types.entry_invalid;
- }
- }
-
- TypeTableEntry *implicit_type = nullptr;
- if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) {
- implicit_type = explicit_type;
- } else if (variable_declaration->expr) {
- implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
- if (implicit_type->id == TypeTableEntryIdInvalid) {
- // ignore the poison value
- } else if (expr_is_maybe) {
- if (implicit_type->id == TypeTableEntryIdMaybe) {
- if (var_is_ptr) {
- // TODO if the expression is constant, can't get pointer to it
- implicit_type = get_pointer_to_type(g, implicit_type->data.maybe.child_type, false);
- } else {
- implicit_type = implicit_type->data.maybe.child_type;
- }
- } else {
- add_node_error(g, variable_declaration->expr, buf_sprintf("expected maybe type"));
- implicit_type = g->builtin_types.entry_invalid;
- }
- } else if (implicit_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, source_node,
- buf_sprintf("variable initialization is unreachable"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if ((!is_const || is_export) &&
- (implicit_type->id == TypeTableEntryIdNumLitFloat ||
- implicit_type->id == TypeTableEntryIdNumLitInt))
- {
- add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
- add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
- implicit_type = g->builtin_types.entry_invalid;
- }
- if (implicit_type->id != TypeTableEntryIdInvalid && !context->fn_entry) {
- ConstExprValue *const_val = &get_resolved_expr(variable_declaration->expr)->const_val;
- if (!const_val->ok) {
- add_node_error(g, first_executing_node(variable_declaration->expr),
- buf_sprintf("global variable initializer requires constant expression"));
- }
- }
- } else if (!is_extern) {
- add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
- implicit_type = g->builtin_types.entry_invalid;
- }
-
- TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
- assert(type != nullptr); // should have been caught by the parser
-
- VariableTableEntry *var = add_local_var(g, source_node, import, context,
- variable_declaration->symbol, type, is_const,
- expr_is_maybe ? nullptr : variable_declaration->expr);
-
- variable_declaration->variable = var;
-
- return var;
-}
-
-static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, TypeTableEntry *expected_type, AstNode *node)
-{
- AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
- return analyze_variable_declaration_raw(g, import, context, node, variable_declaration,
- false, nullptr, false);
-}
-
-static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import,
- BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeNullLiteral);
-
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
- const_val->ok = true;
-
- return g->builtin_types.entry_null;
-}
-
-static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeUndefinedLiteral);
-
- Expr *expr = get_resolved_expr(node);
- ConstExprValue *const_val = &expr->const_val;
-
- const_val->ok = true;
- const_val->special = ConstValSpecialUndef;
-
- return expected_type ? expected_type : g->builtin_types.entry_undef;
-}
-
-static TypeTableEntry *analyze_zeroes_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- Expr *expr = get_resolved_expr(node);
- ConstExprValue *const_val = &expr->const_val;
-
- const_val->ok = true;
- const_val->special = ConstValSpecialZeroes;
-
- return expected_type ? expected_type : g->builtin_types.entry_undef;
-}
-
-static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import,
- BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
-{
- return resolve_expr_const_val_as_bignum(g, node, expected_type, node->data.number_literal.bignum, false);
-}
-
-static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- AstNode *size_node = node->data.array_type.size;
-
- TypeTableEntry *child_type = analyze_type_expr_pointer_only(g, import, context,
- node->data.array_type.child_type, true);
-
- if (child_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, node, buf_create_from_str("array of unreachable not allowed"));
- return g->builtin_types.entry_invalid;
- } else if (child_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- if (size_node) {
- child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type);
- TypeTableEntry *size_type = analyze_expression(g, import, context,
- g->builtin_types.entry_usize, size_node);
- if (size_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
- if (const_val->ok) {
- if (const_val->data.x_bignum.is_negative) {
- add_node_error(g, size_node,
- buf_sprintf("array size %s is negative",
- buf_ptr(bignum_to_buf(&const_val->data.x_bignum))));
- return g->builtin_types.entry_invalid;
- } else {
- return resolve_expr_const_val_as_type(g, node,
- get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint), false);
- }
- } else if (context->fn_entry) {
- return resolve_expr_const_val_as_type(g, node,
- get_slice_type(g, child_type, node->data.array_type.is_const), false);
- } else {
- add_node_error(g, first_executing_node(size_node),
- buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- }
- } else {
- TypeTableEntry *slice_type = get_slice_type(g, child_type, node->data.array_type.is_const);
- return resolve_expr_const_val_as_type(g, node, slice_type, false);
- }
-}
-
-static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- TypeTableEntry *type_entry = analyze_fn_proto_type(g, import, context, expected_type, node,
- false, false, nullptr);
-
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- }
-
- return resolve_expr_const_val_as_type(g, node, type_entry, false);
-}
-
-static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeWhileExpr);
-
- AstNode **condition_node = &node->data.while_expr.condition;
- AstNode *while_body_node = node->data.while_expr.body;
- AstNode **continue_expr_node = &node->data.while_expr.continue_expr;
-
- TypeTableEntry *condition_type = analyze_expression(g, import, context,
- g->builtin_types.entry_bool, *condition_node);
-
- if (*continue_expr_node) {
- analyze_expression(g, import, context, g->builtin_types.entry_void, *continue_expr_node);
- }
-
- BlockContext *child_context = new_block_context(node, context);
- child_context->parent_loop_node = node;
-
- analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node);
-
-
- TypeTableEntry *expr_return_type = g->builtin_types.entry_void;
-
- if (condition_type->id == TypeTableEntryIdInvalid) {
- expr_return_type = g->builtin_types.entry_invalid;
- } else {
- // if the condition is a simple constant expression and there are no break statements
- // then the return type is unreachable
- ConstExprValue *const_val = &get_resolved_expr(*condition_node)->const_val;
- if (const_val->ok) {
- if (const_val->data.x_bool) {
- node->data.while_expr.condition_always_true = true;
- if (!node->data.while_expr.contains_break) {
- expr_return_type = g->builtin_types.entry_unreachable;
- }
- }
- }
- }
-
- return expr_return_type;
-}
-
-static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeBreak);
-
- AstNode *loop_node = context->parent_loop_node;
- if (loop_node) {
- if (loop_node->type == NodeTypeWhileExpr) {
- loop_node->data.while_expr.contains_break = true;
- } else if (loop_node->type == NodeTypeForExpr) {
- loop_node->data.for_expr.contains_break = true;
- } else {
- zig_unreachable();
- }
- } else {
- add_node_error(g, node, buf_sprintf("'break' expression outside loop"));
- }
- return g->builtin_types.entry_unreachable;
-}
-
-static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- AstNode *loop_node = context->parent_loop_node;
- if (loop_node) {
- if (loop_node->type == NodeTypeWhileExpr) {
- loop_node->data.while_expr.contains_continue = true;
- } else if (loop_node->type == NodeTypeForExpr) {
- loop_node->data.for_expr.contains_continue = true;
- } else {
- zig_unreachable();
- }
- } else {
- add_node_error(g, node, buf_sprintf("'continue' expression outside loop"));
- }
- return g->builtin_types.entry_unreachable;
-}
-
-static TypeTableEntry *add_error_if_type_is_num_lit(CodeGen *g, TypeTableEntry *type_entry, AstNode *source_node) {
- if (type_entry->id == TypeTableEntryIdNumLitInt ||
- type_entry->id == TypeTableEntryIdNumLitFloat)
- {
- add_node_error(g, source_node, buf_sprintf("unable to infer expression type"));
- return g->builtin_types.entry_invalid;
- } else {
- return type_entry;
- }
-}
-
-static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
- TypeTableEntry *expected_type, AstNode *node,
- AstNode **then_node, AstNode **else_node, bool cond_is_const, bool cond_bool_val)
-{
- if (!*else_node) {
- *else_node = create_ast_void_node(g, import, node);
- normalize_parent_ptrs(node);
- }
-
- BlockContext *then_context;
- BlockContext *else_context;
- if (cond_is_const) {
- if (cond_bool_val) {
- then_context = parent_context;
- else_context = new_block_context(node, parent_context);
-
- else_context->codegen_excluded = true;
- } else {
- then_context = new_block_context(node, parent_context);
- else_context = parent_context;
-
- then_context->codegen_excluded = true;
- }
- } else {
- then_context = parent_context;
- else_context = parent_context;
- }
-
- TypeTableEntry *then_type = nullptr;
- TypeTableEntry *else_type = nullptr;
-
- if (!then_context->codegen_excluded) {
- then_type = analyze_expression(g, import, then_context, expected_type, *then_node);
- if (then_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
- }
- if (!else_context->codegen_excluded) {
- else_type = analyze_expression(g, import, else_context, expected_type, *else_node);
- if (else_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
- }
-
- TypeTableEntry *result_type;
- if (then_context->codegen_excluded) {
- result_type = else_type;
- } else if (else_context->codegen_excluded) {
- result_type = then_type;
- } else if (expected_type) {
- result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
- } else {
- AstNode *op_nodes[] = {*then_node, *else_node};
- TypeTableEntry *op_types[] = {then_type, else_type};
- result_type = resolve_peer_type_compatibility(g, import, parent_context, node, op_nodes, op_types, 2);
- }
-
- if (!cond_is_const) {
- return add_error_if_type_is_num_lit(g, result_type, node);
- }
-
- ConstExprValue *other_const_val;
- if (cond_bool_val) {
- other_const_val = &get_resolved_expr(*then_node)->const_val;
- } else {
- other_const_val = &get_resolved_expr(*else_node)->const_val;
- }
- if (!other_const_val->ok) {
- return add_error_if_type_is_num_lit(g, result_type, node);
- }
-
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
- *const_val = *other_const_val;
- // the condition depends on a compile var, so the entire if statement does too
- const_val->depends_on_compile_var = true;
- return result_type;
-}
-
-static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- AstNode **cond = &node->data.if_bool_expr.condition;
- TypeTableEntry *cond_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *cond);
-
- if (cond_type->id == TypeTableEntryIdInvalid) {
- return cond_type;
- }
-
- ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val;
- if (cond_val->special == ConstValSpecialUndef) {
- add_node_error(g, first_executing_node(*cond), buf_sprintf("branch on undefined value"));
- return cond_type;
- }
- if (cond_val->ok && !cond_val->depends_on_compile_var) {
- const char *str_val = cond_val->data.x_bool ? "true" : "false";
- add_node_error(g, first_executing_node(*cond),
- buf_sprintf("condition is always %s; unnecessary if statement", str_val));
- }
-
- bool cond_is_const = cond_val->ok;
- bool cond_bool_val = cond_val->data.x_bool;
-
- AstNode **then_node = &node->data.if_bool_expr.then_block;
- AstNode **else_node = &node->data.if_bool_expr.else_node;
-
- return analyze_if(g, import, context, expected_type, node,
- then_node, else_node, cond_is_const, cond_bool_val);
-}
-
-static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeIfVarExpr);
-
- BlockContext *child_context = new_block_context(node, parent_context);
-
- analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true,
- nullptr, node->data.if_var_expr.var_is_ptr);
- VariableTableEntry *var = node->data.if_var_expr.var_decl.variable;
- if (var->type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
- AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr;
- ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val;
- bool cond_is_const = var_const_val->ok;
- bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false;
-
-
- AstNode **then_node = &node->data.if_var_expr.then_block;
- AstNode **else_node = &node->data.if_var_expr.else_node;
-
- return analyze_if(g, import, child_context, expected_type,
- node, then_node, else_node, cond_is_const, cond_bool_val);
-}
-
bool type_is_codegen_pointer(TypeTableEntry *type) {
if (type->id == TypeTableEntryIdPointer) return true;
if (type->id == TypeTableEntryIdFn) return true;
@@ -4192,1066 +2501,7 @@ bool type_is_codegen_pointer(TypeTableEntry *type) {
return false;
}
-static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type,
- TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry)
-{
- ErrorMsg *msg = add_node_error(g, node,
- buf_sprintf("function called as method of '%s', but first parameter is of type '%s'",
- buf_ptr(&container_type->name),
- buf_ptr(&expected_param_type->name)));
- if (fn_table_entry) {
- add_error_note(g, msg, fn_table_entry->proto_node, buf_sprintf("function declared here"));
- }
- return g->builtin_types.entry_invalid;
-}
-
-// Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known
-// at compile time. Otherwise this is a function pointer call.
-static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type,
- AstNode *struct_node)
-{
- assert(node->type == NodeTypeFnCallExpr);
-
- if (fn_type->id == TypeTableEntryIdInvalid) {
- return fn_type;
- }
-
- // The function call might include inline parameters which we need to ignore according to the
- // fn_type.
- FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
- AstNode *generic_proto_node = fn_table_entry ?
- fn_table_entry->proto_node->data.fn_proto.generic_proto_node : nullptr;
-
- // count parameters
- size_t struct_node_1_or_0 = struct_node ? 1 : 0;
- size_t src_param_count = fn_type->data.fn.fn_type_id.param_count +
- (generic_proto_node ? generic_proto_node->data.fn_proto.inline_arg_count : 0);
- size_t call_param_count = node->data.fn_call_expr.params.length;
- size_t expect_arg_count = src_param_count - struct_node_1_or_0;
-
- bool ok_invocation = true;
-
- if (fn_type->data.fn.fn_type_id.is_var_args) {
- if (call_param_count < expect_arg_count) {
- ok_invocation = false;
- add_node_error(g, node,
- buf_sprintf("expected at least %zu arguments, got %zu", src_param_count, call_param_count));
- }
- } else if (expect_arg_count != call_param_count) {
- ok_invocation = false;
- add_node_error(g, node,
- buf_sprintf("expected %zu arguments, got %zu", expect_arg_count, call_param_count));
- }
-
- bool all_args_const_expr = true;
-
- if (struct_node) {
- Expr *struct_expr = get_resolved_expr(struct_node);
- ConstExprValue *struct_const_val = &struct_expr->const_val;
- if (!struct_const_val->ok) {
- all_args_const_expr = false;
- }
-
- FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[0];
- TypeTableEntry *expected_param_type = param_info->type;
- TypeTableEntry *container_bare_type = container_ref_type(struct_expr->type_entry);
- if (is_container_ref(expected_param_type)) {
- TypeTableEntry *param_bare_type = container_ref_type(expected_param_type);
- if (param_bare_type != container_bare_type) {
- return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
- }
- } else {
- return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
- }
- }
-
- // analyze each parameter. in the case of a method, we already analyzed the
- // first parameter in order to figure out which struct we were calling a method on.
- size_t next_type_i = struct_node_1_or_0;
- for (size_t call_i = 0; call_i < call_param_count; call_i += 1) {
- size_t proto_i = call_i + struct_node_1_or_0;
- AstNode **param_node = &node->data.fn_call_expr.params.at(call_i);
- // determine the expected type for each parameter
- TypeTableEntry *expected_param_type = nullptr;
- if (proto_i < src_param_count) {
- if (generic_proto_node &&
- generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
- {
- continue;
- }
-
- FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[next_type_i];
- next_type_i += 1;
-
- expected_param_type = param_info->type;
- }
- TypeTableEntry *param_type = analyze_expression(g, import, context, expected_param_type, *param_node);
- if (param_type->id == TypeTableEntryIdInvalid) {
- return param_type;
- }
-
- ConstExprValue *const_arg_val = &get_resolved_expr(*param_node)->const_val;
- if (!const_arg_val->ok) {
- all_args_const_expr = false;
- }
- }
-
- TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
-
- if (return_type->id == TypeTableEntryIdInvalid) {
- return return_type;
- }
-
- ConstExprValue *result_val = &get_resolved_expr(node)->const_val;
- if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {
- if (fn_table_entry->anal_state == FnAnalStateReady) {
- analyze_fn_body(g, fn_table_entry);
- if (fn_table_entry->proto_node->data.fn_proto.skip) {
- return g->builtin_types.entry_invalid;
- }
- }
- if (all_args_const_expr) {
- if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) {
- if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) {
- // function evaluation generated an error
- return g->builtin_types.entry_invalid;
- }
- return return_type;
- }
- }
- }
- if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure || fn_table_entry->want_pure == WantPureFalse) {
- // calling an impure fn is impure
- mark_impure_fn(g, context, node);
- if (fn_table_entry && fn_table_entry->want_pure == WantPureTrue) {
- return g->builtin_types.entry_invalid;
- }
- }
-
- // TODO
- //if (handle_is_ptr(return_type)) {
- // if (context->fn_entry) {
- // context->fn_entry->cast_alloca_list.append(node);
- // } else if (!result_val->ok) {
- // add_node_error(g, node, buf_sprintf("unable to evaluate constant expression"));
- // }
- //}
-
- return return_type;
-}
-
-static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableEntry *import,
- BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *call_node,
- FnTableEntry *fn_table_entry, AstNode *struct_node)
-{
- assert(call_node->type == NodeTypeFnCallExpr);
- assert(fn_table_entry);
-
- AstNode *decl_node = fn_table_entry->proto_node;
-
- // count parameters
- size_t struct_node_1_or_0 = (struct_node ? 1 : 0);
- size_t src_param_count = decl_node->data.fn_proto.params.length;
- size_t call_param_count = call_node->data.fn_call_expr.params.length;
-
- if (src_param_count != call_param_count + struct_node_1_or_0) {
- add_node_error(g, call_node,
- buf_sprintf("expected %zu arguments, got %zu", src_param_count - struct_node_1_or_0, call_param_count));
- return g->builtin_types.entry_invalid;
- }
-
- size_t inline_or_var_type_arg_count = decl_node->data.fn_proto.inline_or_var_type_arg_count;
- assert(inline_or_var_type_arg_count > 0);
-
- BlockContext *child_context = decl_node->owner->block_context;
- size_t next_generic_param_index = 0;
-
- GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1);
- generic_fn_type_id->decl_node = decl_node;
- generic_fn_type_id->generic_param_count = inline_or_var_type_arg_count;
- generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_or_var_type_arg_count);
-
- size_t next_impl_i = 0;
- for (size_t call_i = 0; call_i < call_param_count; call_i += 1) {
- size_t proto_i = call_i + struct_node_1_or_0;
- AstNode *generic_param_decl_node = decl_node->data.fn_proto.params.at(proto_i);
- assert(generic_param_decl_node->type == NodeTypeParamDecl);
-
- AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type;
- TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner, child_context,
- *generic_param_type_node);
- if (expected_param_type->id == TypeTableEntryIdInvalid) {
- return expected_param_type;
- }
-
- bool is_var_type = (expected_param_type->id == TypeTableEntryIdVar);
- bool is_inline = generic_param_decl_node->data.param_decl.is_inline;
- if (!is_inline && !is_var_type) {
- next_impl_i += 1;
- continue;
- }
-
-
- AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i);
- TypeTableEntry *param_type = analyze_expression(g, import, parent_context,
- is_var_type ? nullptr : expected_param_type, *param_node);
- if (param_type->id == TypeTableEntryIdInvalid) {
- return param_type;
- }
-
- // set child_context so that the previous param is in scope
- child_context = new_block_context(generic_param_decl_node, child_context);
-
- ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val;
- if (is_inline && !const_val->ok) {
- add_node_error(g, *param_node,
- buf_sprintf("unable to evaluate constant expression for inline parameter"));
-
- return g->builtin_types.entry_invalid;
- }
-
- VariableTableEntry *var = add_local_var_shadowable(g, generic_param_decl_node, decl_node->owner, child_context,
- generic_param_decl_node->data.param_decl.name, param_type, true, *param_node, true);
- // This generic function instance could be called with anything, so when this variable is read it
- // needs to know that it depends on compile time variable data.
- var->force_depends_on_compile_var = true;
-
- GenericParamValue *generic_param_value =
- &generic_fn_type_id->generic_params[next_generic_param_index];
- generic_param_value->type = param_type;
- generic_param_value->node = is_inline ? *param_node : nullptr;
- generic_param_value->impl_index = next_impl_i;
- next_generic_param_index += 1;
-
- if (!is_inline) {
- next_impl_i += 1;
- }
- }
-
- assert(next_generic_param_index == inline_or_var_type_arg_count);
-
- auto entry = g->generic_table.maybe_get(generic_fn_type_id);
- FnTableEntry *impl_fn;
- if (entry) {
- AstNode *impl_decl_node = entry->value;
- assert(impl_decl_node->type == NodeTypeFnProto);
- impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
- } else {
- AstNode *decl_node = generic_fn_type_id->decl_node;
- AstNode *impl_fn_def_node = ast_clone_subtree_special(decl_node->data.fn_proto.fn_def_node,
- &g->next_node_index, AstCloneSpecialOmitInlineParams);
- AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto;
- impl_decl_node->data.fn_proto.inline_arg_count = 0;
- impl_decl_node->data.fn_proto.inline_or_var_type_arg_count = 0;
- impl_decl_node->data.fn_proto.generic_proto_node = decl_node;
-
- // replace var arg types with actual types
- for (size_t generic_arg_i = 0; generic_arg_i < inline_or_var_type_arg_count; generic_arg_i += 1) {
- GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[generic_arg_i];
- if (!generic_param_value->node) {
- size_t impl_i = generic_param_value->impl_index;
- AstNode *impl_param_decl_node = impl_decl_node->data.fn_proto.params.at(impl_i);
- assert(impl_param_decl_node->type == NodeTypeParamDecl);
-
- impl_param_decl_node->data.param_decl.type = create_ast_type_node(g, import,
- generic_param_value->type, impl_param_decl_node);
- normalize_parent_ptrs(impl_param_decl_node);
- }
- }
-
- preview_fn_proto_instance(g, import, impl_decl_node, child_context);
- g->generic_table.put(generic_fn_type_id, impl_decl_node);
- impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
- }
-
- call_node->data.fn_call_expr.fn_entry = impl_fn;
- return analyze_fn_call_ptr(g, import, parent_context, expected_type, call_node,
- impl_fn->type_entry, struct_node);
-}
-
-static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
- TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *generic_fn_type)
-{
- assert(node->type == NodeTypeFnCallExpr);
- assert(generic_fn_type->id == TypeTableEntryIdGenericFn);
-
- AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
- assert(decl_node->type == NodeTypeContainerDecl);
- ZigList<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params;
-
- size_t expected_param_count = generic_params->length;
- size_t actual_param_count = node->data.fn_call_expr.params.length;
-
- if (actual_param_count != expected_param_count) {
- add_node_error(g, first_executing_node(node),
- buf_sprintf("expected %zu arguments, got %zu", expected_param_count, actual_param_count));
- return g->builtin_types.entry_invalid;
- }
-
- GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1);
- generic_fn_type_id->decl_node = decl_node;
- generic_fn_type_id->generic_param_count = actual_param_count;
- generic_fn_type_id->generic_params = allocate<GenericParamValue>(actual_param_count);
-
- BlockContext *child_context = decl_node->owner->block_context;
- for (size_t i = 0; i < actual_param_count; i += 1) {
- AstNode *generic_param_decl_node = generic_params->at(i);
- assert(generic_param_decl_node->type == NodeTypeParamDecl);
- AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type;
-
- TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner,
- child_context, *generic_param_type_node);
- if (expected_param_type->id == TypeTableEntryIdInvalid) {
- return expected_param_type;
- }
-
-
-
- AstNode **param_node = &node->data.fn_call_expr.params.at(i);
-
- TypeTableEntry *param_type = analyze_expression(g, import, parent_context, expected_param_type,
- *param_node);
- if (param_type->id == TypeTableEntryIdInvalid) {
- return param_type;
- }
-
- // set child_context so that the previous param is in scope
- child_context = new_block_context(generic_param_decl_node, child_context);
-
- ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val;
- if (const_val->ok) {
- VariableTableEntry *var = add_local_var(g, generic_param_decl_node, decl_node->owner, child_context,
- generic_param_decl_node->data.param_decl.name, param_type, true, *param_node);
- var->force_depends_on_compile_var = true;
- } else {
- add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression"));
-
- return g->builtin_types.entry_invalid;
- }
-
- GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[i];
- generic_param_value->type = param_type;
- generic_param_value->node = *param_node;
- }
-
- auto entry = g->generic_table.maybe_get(generic_fn_type_id);
- if (entry) {
- AstNode *impl_decl_node = entry->value;
- assert(impl_decl_node->type == NodeTypeContainerDecl);
- TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
- return resolve_expr_const_val_as_type(g, node, type_entry, false);
- }
-
- // make a type from the generic parameters supplied
- assert(decl_node->type == NodeTypeContainerDecl);
- AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index);
- g->generic_table.put(generic_fn_type_id, impl_decl_node);
- scan_struct_decl(g, import, child_context, impl_decl_node);
- TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
- resolve_struct_type(g, import, type_entry);
- return resolve_expr_const_val_as_type(g, node, type_entry, false);
-}
-
-static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-
- if (node->data.fn_call_expr.is_builtin) {
- zig_panic("moved builtin fn call code to ir.cpp");
- }
-
- TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);
- if (invoke_type_entry->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- // use constant expression evaluator to figure out the function at compile time.
- // otherwise we treat this as a function pointer.
- ConstExprValue *const_val = &get_resolved_expr(fn_ref_expr)->const_val;
-
- if (const_val->ok) {
- if (invoke_type_entry->id == TypeTableEntryIdMetaType) {
- zig_unreachable();
- } else if (invoke_type_entry->id == TypeTableEntryIdFn) {
- AstNode *struct_node;
- if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
- fn_ref_expr->data.field_access_expr.is_member_fn)
- {
- struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
- } else {
- struct_node = nullptr;
- }
-
- FnTableEntry *fn_table_entry = const_val->data.x_fn;
- node->data.fn_call_expr.fn_entry = fn_table_entry;
- return analyze_fn_call_ptr(g, import, context, expected_type, node,
- fn_table_entry->type_entry, struct_node);
- } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) {
- TypeTableEntry *generic_fn_type = const_val->data.x_type;
- AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
- if (decl_node->type == NodeTypeFnProto) {
- AstNode *struct_node;
- if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
- fn_ref_expr->data.field_access_expr.is_member_fn)
- {
- struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
- } else {
- struct_node = nullptr;
- }
-
- FnTableEntry *fn_table_entry = decl_node->data.fn_proto.fn_table_entry;
- if (fn_table_entry->proto_node->data.fn_proto.skip) {
- return g->builtin_types.entry_invalid;
- }
- return analyze_fn_call_with_inline_args(g, import, context, expected_type, node,
- fn_table_entry, struct_node);
- } else {
- return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type);
- }
- } else {
- add_node_error(g, fn_ref_expr,
- buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
- return g->builtin_types.entry_invalid;
- }
- }
-
- // function pointer
- if (invoke_type_entry->id == TypeTableEntryIdFn) {
- return analyze_fn_call_ptr(g, import, context, expected_type, node, invoke_type_entry, nullptr);
- } else {
- add_node_error(g, fn_ref_expr,
- buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
- return g->builtin_types.entry_invalid;
- }
-}
-
-static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- AstNode **expr_node = &node->data.switch_expr.expr;
- TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node);
- ConstExprValue *expr_val = &get_resolved_expr(*expr_node)->const_val;
- if (expr_val->ok && !expr_val->depends_on_compile_var) {
- add_node_error(g, first_executing_node(*expr_node),
- buf_sprintf("value is constant; unnecessary switch statement"));
- }
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-
-
- size_t prong_count = node->data.switch_expr.prongs.length;
- AstNode **peer_nodes = allocate<AstNode*>(prong_count);
- TypeTableEntry **peer_types = allocate<TypeTableEntry*>(prong_count);
-
- bool any_errors = false;
- if (expr_type->id == TypeTableEntryIdInvalid) {
- return expr_type;
- } else if (expr_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, first_executing_node(*expr_node),
- buf_sprintf("switch on unreachable expression not allowed"));
- return g->builtin_types.entry_invalid;
- }
-
-
- size_t *field_use_counts = nullptr;
- HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes = {};
- if (expr_type->id == TypeTableEntryIdEnum) {
- field_use_counts = allocate<size_t>(expr_type->data.enumeration.src_field_count);
- } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
- err_use_nodes.init(10);
- }
-
- size_t *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index;
- *const_chosen_prong_index = SIZE_MAX;
- AstNode *else_prong = nullptr;
- for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
- AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
-
- TypeTableEntry *var_type;
- bool var_is_target_expr;
- if (prong_node->data.switch_prong.items.length == 0) {
- if (else_prong) {
- add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression"));
- any_errors = true;
- } else {
- else_prong = prong_node;
- }
- var_type = expr_type;
- var_is_target_expr = true;
- if (*const_chosen_prong_index == SIZE_MAX && expr_val->ok) {
- *const_chosen_prong_index = prong_i;
- }
- } else {
- bool all_agree_on_var_type = true;
- var_type = nullptr;
-
- for (size_t item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {
- AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
- if (item_node->type == NodeTypeSwitchRange) {
- zig_panic("TODO range in switch statement");
- }
-
- if (expr_type->id == TypeTableEntryIdEnum) {
- if (item_node->type == NodeTypeSymbol) {
- Buf *field_name = item_node->data.symbol_expr.symbol;
- TypeEnumField *type_enum_field = find_enum_type_field(expr_type, field_name);
- if (type_enum_field) {
- item_node->data.symbol_expr.enum_field = type_enum_field;
- if (!var_type) {
- var_type = type_enum_field->type_entry;
- }
- if (type_enum_field->type_entry != var_type) {
- all_agree_on_var_type = false;
- }
- uint32_t field_index = type_enum_field->value;
- assert(field_use_counts);
- field_use_counts[field_index] += 1;
- if (field_use_counts[field_index] > 1) {
- add_node_error(g, item_node,
- buf_sprintf("duplicate switch value: '%s'",
- buf_ptr(type_enum_field->name)));
- any_errors = true;
- }
- if (!any_errors && expr_val->ok) {
- if (expr_val->data.x_enum.tag == type_enum_field->value) {
- *const_chosen_prong_index = prong_i;
- }
- }
- } else {
- add_node_error(g, item_node,
- buf_sprintf("enum '%s' has no field '%s'",
- buf_ptr(&expr_type->name), buf_ptr(field_name)));
- any_errors = true;
- }
- } else {
- add_node_error(g, item_node, buf_sprintf("expected enum tag name"));
- any_errors = true;
- }
- } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
- if (item_node->type == NodeTypeSymbol) {
- Buf *err_name = item_node->data.symbol_expr.symbol;
- bool is_ok_case = buf_eql_str(err_name, "Ok");
- auto err_table_entry = is_ok_case ? nullptr: g->error_table.maybe_get(err_name);
- if (is_ok_case || err_table_entry) {
- uint32_t err_value = is_ok_case ? 0 : err_table_entry->value->value;
- item_node->data.symbol_expr.err_value = err_value;
- TypeTableEntry *this_var_type;
- if (is_ok_case) {
- this_var_type = expr_type->data.error.child_type;
- } else {
- this_var_type = g->builtin_types.entry_pure_error;
- }
- if (!var_type) {
- var_type = this_var_type;
- }
- if (this_var_type != var_type) {
- all_agree_on_var_type = false;
- }
-
- // detect duplicate switch values
- auto existing_entry = err_use_nodes.maybe_get(err_value);
- if (existing_entry) {
- add_node_error(g, existing_entry->value,
- buf_sprintf("duplicate switch value: '%s'", buf_ptr(err_name)));
- any_errors = true;
- } else {
- err_use_nodes.put(err_value, item_node);
- }
-
- if (!any_errors && expr_val->ok) {
- if (expr_val->data.x_err.err->value == err_value) {
- *const_chosen_prong_index = prong_i;
- }
- }
- } else {
- add_node_error(g, item_node,
- buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name)));
- any_errors = true;
- }
- } else {
- add_node_error(g, item_node, buf_sprintf("expected error value name"));
- any_errors = true;
- }
- } else {
- if (!any_errors && expr_val->ok) {
- // note: there is now a function in eval.cpp for doing const expr comparison
- zig_panic("TODO determine if const exprs are equal");
- }
- TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node);
- if (item_type->id != TypeTableEntryIdInvalid) {
- ConstExprValue *const_val = &get_resolved_expr(item_node)->const_val;
- if (!const_val->ok) {
- add_node_error(g, item_node,
- buf_sprintf("unable to evaluate constant expression"));
- any_errors = true;
- }
- }
- }
- }
- if (!var_type || !all_agree_on_var_type) {
- var_type = expr_type;
- var_is_target_expr = true;
- } else {
- var_is_target_expr = false;
- }
- }
-
- BlockContext *child_context = new_block_context(node, context);
- prong_node->data.switch_prong.block_context = child_context;
- AstNode *var_node = prong_node->data.switch_prong.var_symbol;
- if (var_node) {
- assert(var_node->type == NodeTypeSymbol);
- Buf *var_name = var_node->data.symbol_expr.symbol;
- var_node->block_context = child_context;
- prong_node->data.switch_prong.var = add_local_var(g, var_node, import,
- child_context, var_name, var_type, true, nullptr);
- prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr;
- }
- }
-
- for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
- AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
- BlockContext *child_context = prong_node->data.switch_prong.block_context;
- child_context->codegen_excluded = expr_val->ok && (*const_chosen_prong_index != prong_i);
-
- if (child_context->codegen_excluded) {
- peer_types[prong_i] = g->builtin_types.entry_unreachable;
- } else {
- peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,
- prong_node->data.switch_prong.expr);
- }
- // This must go after the analyze_expression for
- // prong_node->data.switch_prong.expr because of AST rewriting.
- peer_nodes[prong_i] = prong_node->data.switch_prong.expr;
- }
-
- if (expr_type->id == TypeTableEntryIdEnum && !else_prong) {
- for (uint32_t i = 0; i < expr_type->data.enumeration.src_field_count; i += 1) {
- if (field_use_counts[i] == 0) {
- add_node_error(g, node,
- buf_sprintf("enumeration value '%s' not handled in switch",
- buf_ptr(expr_type->data.enumeration.fields[i].name)));
- any_errors = true;
- }
- }
- }
-
- if (any_errors) {
- return g->builtin_types.entry_invalid;
- }
-
- if (prong_count == 0) {
- add_node_error(g, node, buf_sprintf("switch statement has no prongs"));
- return g->builtin_types.entry_invalid;
- }
-
- TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node,
- peer_nodes, peer_types, prong_count);
-
- if (expr_val->ok) {
- assert(*const_chosen_prong_index != SIZE_MAX);
-
- *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val;
- // the target expr depends on a compile var because we have an error on unnecessary
- // switch statement, so the entire switch statement does too
- const_val->depends_on_compile_var = true;
-
- if (!const_val->ok) {
- return add_error_if_type_is_num_lit(g, result_type, node);
- }
- } else {
- return add_error_if_type_is_num_lit(g, result_type, node);
- }
-
- return result_type;
-}
-
-static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- if (!node->data.return_expr.expr) {
- node->data.return_expr.expr = create_ast_void_node(g, import, node);
- normalize_parent_ptrs(node);
- }
-
- TypeTableEntry *expected_return_type = get_return_type(context);
-
- switch (node->data.return_expr.kind) {
- case ReturnKindUnconditional:
- zig_panic("TODO moved to ir.cpp");
- case ReturnKindError:
- {
- TypeTableEntry *expected_err_type;
- if (expected_type) {
- expected_err_type = get_error_type(g, expected_type);
- } else {
- expected_err_type = nullptr;
- }
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_err_type,
- node->data.return_expr.expr);
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- } else if (resolved_type->id == TypeTableEntryIdErrorUnion) {
- if (expected_return_type->id != TypeTableEntryIdErrorUnion &&
- expected_return_type->id != TypeTableEntryIdPureError)
- {
- ErrorMsg *msg = add_node_error(g, node,
- buf_sprintf("%%return statement in function with return type '%s'",
- buf_ptr(&expected_return_type->name)));
- AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
- add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
- }
-
- return resolved_type->data.error.child_type;
- } else {
- add_node_error(g, node->data.return_expr.expr,
- buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case ReturnKindMaybe:
- {
- TypeTableEntry *expected_maybe_type;
- if (expected_type) {
- expected_maybe_type = get_maybe_type(g, expected_type);
- } else {
- expected_maybe_type = nullptr;
- }
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type,
- node->data.return_expr.expr);
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- } else if (resolved_type->id == TypeTableEntryIdMaybe) {
- if (expected_return_type->id != TypeTableEntryIdMaybe) {
- ErrorMsg *msg = add_node_error(g, node,
- buf_sprintf("?return statement in function with return type '%s'",
- buf_ptr(&expected_return_type->name)));
- AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
- add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
- }
-
- return resolved_type->data.maybe.child_type;
- } else {
- add_node_error(g, node->data.return_expr.expr,
- buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- }
- zig_unreachable();
-}
-
-static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
- if (type_entry->id == TypeTableEntryIdMetaType) {
- add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type"));
- } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
- add_node_error(g, first_executing_node(source_node), buf_sprintf("statement ignores error value"));
- }
-}
-
-static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- if (!parent_context->fn_entry) {
- add_node_error(g, node, buf_sprintf("defer expression outside function definition"));
- return g->builtin_types.entry_invalid;
- }
-
- if (!node->data.defer.expr) {
- add_node_error(g, node, buf_sprintf("defer expects an expression"));
- return g->builtin_types.entry_void;
- }
-
- node->data.defer.child_block = new_block_context(node, parent_context);
-
- TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr,
- node->data.defer.expr);
- validate_voided_expr(g, node->data.defer.expr, resolved_type);
-
- return g->builtin_types.entry_void;
-}
-
-static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- if (node->data.string_literal.c) {
- return resolve_expr_const_val_as_c_string_lit(g, node, node->data.string_literal.buf);
- } else {
- return resolve_expr_const_val_as_string_lit(g, node, node->data.string_literal.buf);
- }
-}
-
-static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- BlockContext *child_context = new_block_context(node, parent_context);
- node->data.block.child_block = child_context;
- TypeTableEntry *return_type = g->builtin_types.entry_void;
-
- for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
- AstNode *child = node->data.block.statements.at(i);
- if (child->type == NodeTypeLabel) {
- FnTableEntry *fn_table_entry = child_context->fn_entry;
- assert(fn_table_entry);
-
- LabelTableEntry *label = allocate<LabelTableEntry>(1);
- label->decl_node = child;
- label->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable);
-
- child->block_context = child_context;
- child->data.label.label_entry = label;
- fn_table_entry->all_labels.append(label);
-
- child_context->label_table.put(child->data.label.name, label);
-
- return_type = g->builtin_types.entry_void;
- continue;
- }
- if (return_type->id == TypeTableEntryIdUnreachable) {
- if (is_node_void_expr(child)) {
- // {unreachable;void;void} is allowed.
- // ignore void statements once we enter unreachable land.
- analyze_expression(g, import, child_context, g->builtin_types.entry_void, child);
- continue;
- }
- add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code"));
- break;
- }
- bool is_last = (i == node->data.block.statements.length - 1);
- TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
- return_type = analyze_expression(g, import, child_context, passed_expected_type, child);
- if (child->type == NodeTypeDefer && return_type->id != TypeTableEntryIdInvalid) {
- // defer starts a new block context
- child_context = child->data.defer.child_block;
- assert(child_context);
- }
- if (!is_last) {
- validate_voided_expr(g, child, return_type);
- }
- }
- node->data.block.nested_block = child_context;
-
- ConstExprValue *const_val = &node->data.block.resolved_expr.const_val;
- if (node->data.block.statements.length == 0) {
- const_val->ok = true;
- } else if (node->data.block.statements.length == 1) {
- AstNode *only_node = node->data.block.statements.at(0);
- ConstExprValue *other_const_val = &get_resolved_expr(only_node)->const_val;
- if (other_const_val->ok) {
- *const_val = *other_const_val;
- }
- }
-
- return return_type;
-}
-
-static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- mark_impure_fn(g, context, node);
-
- node->data.asm_expr.return_count = 0;
- TypeTableEntry *return_type = g->builtin_types.entry_void;
- for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
- AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
- if (asm_output->return_type) {
- node->data.asm_expr.return_count += 1;
- return_type = analyze_type_expr(g, import, context, asm_output->return_type);
- if (node->data.asm_expr.return_count > 1) {
- add_node_error(g, node,
- buf_sprintf("inline assembly allows up to one output value"));
- break;
- }
- } else {
- Buf *variable_name = asm_output->variable_name;
- VariableTableEntry *var = find_variable(g, context, variable_name);
- if (var) {
- asm_output->variable = var;
- return var->type;
- } else {
- add_node_error(g, node,
- buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
- return g->builtin_types.entry_invalid;
- }
- }
- }
- for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
- AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
- analyze_expression(g, import, context, nullptr, asm_input->expr);
- }
-
- return return_type;
-}
-
-static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeGoto);
-
- FnTableEntry *fn_table_entry = context->fn_entry;
- assert(fn_table_entry);
-
- fn_table_entry->goto_list.append(node);
-
- return g->builtin_types.entry_unreachable;
-}
-
-static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
-{
- assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid);
- TypeTableEntry *return_type = nullptr;
- node->block_context = context;
- switch (node->type) {
- case NodeTypeBlock:
- return_type = analyze_block_expr(g, import, context, expected_type, node);
- break;
-
- case NodeTypeReturnExpr:
- return_type = analyze_return_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeDefer:
- return_type = analyze_defer(g, import, context, expected_type, node);
- break;
- case NodeTypeVariableDeclaration:
- analyze_variable_declaration(g, import, context, expected_type, node);
- return_type = g->builtin_types.entry_void;
- break;
- case NodeTypeGoto:
- return_type = analyze_goto_pass1(g, import, context, expected_type, node);
- break;
- case NodeTypeBreak:
- return_type = analyze_break_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeContinue:
- return_type = analyze_continue_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeAsmExpr:
- return_type = analyze_asm_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeBinOpExpr:
- return_type = analyze_bin_op_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeUnwrapErrorExpr:
- return_type = analyze_unwrap_error_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeFnCallExpr:
- return_type = analyze_fn_call_expr(g, import, context, expected_type, node);
- break;
-
- case NodeTypeArrayAccessExpr:
- // for reading array access; assignment handled elsewhere
- return_type = analyze_array_access_expr(g, import, context, node, LValPurposeAddressOf);
- break;
- case NodeTypeSliceExpr:
- return_type = analyze_slice_expr(g, import, context, node);
- break;
- case NodeTypeFieldAccessExpr:
- return_type = analyze_field_access_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeContainerInitExpr:
- zig_panic("analyze container init moved to ir.cpp");
- break;
- case NodeTypeNumberLiteral:
- return_type = analyze_number_literal_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeStringLiteral:
- return_type = analyze_string_literal_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeCharLiteral:
- return_type = resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
- node->data.char_literal.value, false);
- break;
- case NodeTypeBoolLiteral:
- zig_panic("moved to ir.cpp");
- break;
- case NodeTypeNullLiteral:
- return_type = analyze_null_literal_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeUndefinedLiteral:
- return_type = analyze_undefined_literal_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeZeroesLiteral:
- return_type = analyze_zeroes_literal_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeThisLiteral:
- zig_panic("moved to ir.cpp");
- break;
- case NodeTypeSymbol:
- return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only);
- break;
- case NodeTypePrefixOpExpr:
- zig_panic("moved to ir.cpp");
- break;
- case NodeTypeIfBoolExpr:
- return_type = analyze_if_bool_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeIfVarExpr:
- return_type = analyze_if_var_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeWhileExpr:
- return_type = analyze_while_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeForExpr:
- zig_panic("moved to ir.cpp");
- break;
- case NodeTypeArrayType:
- return_type = analyze_array_type(g, import, context, expected_type, node);
- break;
- case NodeTypeFnProto:
- return_type = analyze_fn_proto_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeErrorType:
- return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_pure_error, false);
- break;
- case NodeTypeTypeLiteral:
- return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_type, false);
- break;
- case NodeTypeSwitchExpr:
- return_type = analyze_switch_expr(g, import, context, expected_type, node);
- break;
- case NodeTypeVarLiteral:
- return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_var, false);
- break;
- case NodeTypeSwitchProng:
- case NodeTypeSwitchRange:
- case NodeTypeFnDecl:
- case NodeTypeParamDecl:
- case NodeTypeRoot:
- case NodeTypeFnDef:
- case NodeTypeUse:
- case NodeTypeLabel:
- case NodeTypeContainerDecl:
- case NodeTypeStructField:
- case NodeTypeStructValueField:
- case NodeTypeErrorValueDecl:
- case NodeTypeTypeDecl:
- zig_unreachable();
- }
- assert(return_type);
- // resolve_type_compatibility might do implicit cast which means node is now a child
- // of the actual node that we want to return the type of.
- //AstNode **field = node->parent_field;
- TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, context, node,
- expected_type, return_type);
-
- Expr *expr = get_resolved_expr(node);
- expr->type_entry = return_type;
-
- add_global_const_expr(g, node);
-
- return resolved_type;
-}
-
-// When you call analyze_expression, the node you pass might no longer be the child node
-// you thought it was due to implicit casting rewriting the AST.
-static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- return analyze_expression_pointer_only(g, import, context, expected_type, node, false);
-}
static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
ImportTableEntry *import = fn_table_entry->import_entry;
@@ -5272,6 +2522,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
BlockContext *context = node->data.fn_def.block_context;
TypeTableEntry *fn_type = fn_table_entry->type_entry;
+ FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
for (size_t i = 0; i < fn_proto->params.length; i += 1) {
AstNode *param_decl_node = fn_proto->params.at(i);
@@ -5279,7 +2530,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
// define local variables for parameters
AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl;
- TypeTableEntry *type = unwrapped_node_type(param_decl->type);
+ TypeTableEntry *type = fn_type_id->param_info[i].type;
if (param_decl->is_noalias && !type_is_codegen_pointer(type)) {
add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
@@ -5340,162 +2591,6 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
fn_table_entry->anal_state = FnAnalStateComplete;
}
-static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
- AstNode *node, Buf *name)
-{
- assert(import);
-
- TopLevelDecl *tld = get_as_top_level_decl(node);
- tld->import = import;
- tld->name = name;
-
- bool want_to_resolve = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport);
- bool is_generic_container = (node->type == NodeTypeContainerDecl &&
- node->data.struct_decl.generic_params.length > 0);
- if (want_to_resolve && !is_generic_container) {
- g->resolve_queue.append(node);
- }
-
- node->block_context = block_context;
-
- auto entry = block_context->decl_table.maybe_get(name);
- if (entry) {
- AstNode *other_decl_node = entry->value;
- ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name)));
- add_error_note(g, msg, other_decl_node, buf_sprintf("previous definition is here"));
- } else {
- block_context->decl_table.put(name, node);
- }
-}
-
-static void count_inline_and_var_args(AstNode *proto_node) {
- assert(proto_node->type == NodeTypeFnProto);
-
- size_t *inline_arg_count = &proto_node->data.fn_proto.inline_arg_count;
- size_t *inline_or_var_type_arg_count = &proto_node->data.fn_proto.inline_or_var_type_arg_count;
-
- *inline_arg_count = 0;
- *inline_or_var_type_arg_count = 0;
-
- // TODO run these nodes through the type analysis system rather than looking for
- // specialized ast nodes. this would get fooled by `{var}` instead of `var` which
- // is supposed to be equivalent
- for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
- AstNode *param_node = proto_node->data.fn_proto.params.at(i);
- assert(param_node->type == NodeTypeParamDecl);
- if (param_node->data.param_decl.is_inline) {
- *inline_arg_count += 1;
- *inline_or_var_type_arg_count += 1;
- } else if (param_node->data.param_decl.type->type == NodeTypeVarLiteral) {
- *inline_or_var_type_arg_count += 1;
- }
- }
-}
-
-static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
- switch (node->type) {
- case NodeTypeRoot:
- for (size_t i = 0; i < import->root->data.root.top_level_decls.length; i += 1) {
- AstNode *child = import->root->data.root.top_level_decls.at(i);
- scan_decls(g, import, context, child);
- }
- break;
- case NodeTypeContainerDecl:
- {
- Buf *name = node->data.struct_decl.name;
- add_top_level_decl(g, import, context, node, name);
- if (node->data.struct_decl.generic_params.length == 0) {
- scan_struct_decl(g, import, context, node);
- }
- }
- break;
- case NodeTypeFnDef:
- node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node;
- scan_decls(g, import, context, node->data.fn_def.fn_proto);
- break;
- case NodeTypeVariableDeclaration:
- {
- Buf *name = node->data.variable_declaration.symbol;
- add_top_level_decl(g, import, context, node, name);
- break;
- }
- case NodeTypeTypeDecl:
- {
- Buf *name = node->data.type_decl.symbol;
- add_top_level_decl(g, import, context, node, name);
- break;
- }
- case NodeTypeFnProto:
- {
- // if the name is missing, we immediately announce an error
- Buf *fn_name = node->data.fn_proto.name;
- if (buf_len(fn_name) == 0) {
- node->data.fn_proto.skip = true;
- add_node_error(g, node, buf_sprintf("missing function name"));
- break;
- }
- count_inline_and_var_args(node);
-
- add_top_level_decl(g, import, context, node, fn_name);
- break;
- }
- case NodeTypeUse:
- {
- TopLevelDecl *tld = get_as_top_level_decl(node);
- tld->import = import;
- node->block_context = context;
- g->use_queue.append(node);
- tld->import->use_decls.append(node);
- break;
- }
- case NodeTypeErrorValueDecl:
- // error value declarations do not depend on other top level decls
- preview_error_value_decl(g, node);
- break;
- case NodeTypeParamDecl:
- case NodeTypeFnDecl:
- case NodeTypeReturnExpr:
- case NodeTypeDefer:
- case NodeTypeBlock:
- case NodeTypeBinOpExpr:
- case NodeTypeUnwrapErrorExpr:
- case NodeTypeFnCallExpr:
- case NodeTypeArrayAccessExpr:
- case NodeTypeSliceExpr:
- case NodeTypeNumberLiteral:
- case NodeTypeStringLiteral:
- case NodeTypeCharLiteral:
- case NodeTypeBoolLiteral:
- case NodeTypeNullLiteral:
- case NodeTypeUndefinedLiteral:
- case NodeTypeZeroesLiteral:
- case NodeTypeThisLiteral:
- case NodeTypeSymbol:
- case NodeTypePrefixOpExpr:
- case NodeTypeIfBoolExpr:
- case NodeTypeIfVarExpr:
- case NodeTypeWhileExpr:
- case NodeTypeForExpr:
- case NodeTypeSwitchExpr:
- case NodeTypeSwitchProng:
- case NodeTypeSwitchRange:
- case NodeTypeLabel:
- case NodeTypeGoto:
- case NodeTypeBreak:
- case NodeTypeContinue:
- case NodeTypeAsmExpr:
- case NodeTypeFieldAccessExpr:
- case NodeTypeStructField:
- case NodeTypeContainerInitExpr:
- case NodeTypeStructValueField:
- case NodeTypeArrayType:
- case NodeTypeErrorType:
- case NodeTypeTypeLiteral:
- case NodeTypeVarLiteral:
- zig_unreachable();
- }
-}
-
static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) {
TopLevelDecl *tld = get_as_top_level_decl(dst_use_node);
AstNode *use_target_node = src_use_node->data.use.expr;
@@ -5565,11 +2660,11 @@ static void resolve_use_decl(CodeGen *g, AstNode *node) {
static void preview_use_decl(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeUse);
TopLevelDecl *tld = get_as_top_level_decl(node);
- TypeTableEntry *use_expr_type = analyze_expression(g, tld->import, tld->import->block_context,
- g->builtin_types.entry_namespace, node->data.use.expr);
- if (use_expr_type->id == TypeTableEntryIdInvalid) {
+
+ IrInstruction *result = analyze_const_value(g, tld->import->block_context, node->data.use.expr,
+ g->builtin_types.entry_namespace);
+ if (result->type_entry->id == TypeTableEntryIdInvalid)
tld->import->any_imports_failed = true;
- }
}
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package,
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 605fe0ae6d..b6bcf5868c 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -45,10 +45,6 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package,
AstNode *first_executing_node(AstNode *node);
-TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import,
- BlockContext *block_context, AstNode *parent_source_node,
- AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count);
-
// TODO move these over, these used to be static
bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type);
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 1a8463e85d..c7459f8ef2 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -261,17 +261,6 @@ static void print_indent(AstRender *ar) {
}
}
-static bool is_node_void(AstNode *node) {
- if (node->type == NodeTypeSymbol) {
- if (node->data.symbol_expr.override_type_entry) {
- return node->data.symbol_expr.override_type_entry->id == TypeTableEntryIdVoid;
- } else if (buf_eql_str(node->data.symbol_expr.symbol, "void")) {
- return true;
- }
- }
- return false;
-}
-
static bool is_alpha_under(uint8_t c) {
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') || c == '_';
@@ -406,10 +395,8 @@ static void render_node(AstRender *ar, AstNode *node) {
fprintf(ar->f, ")");
AstNode *return_type_node = node->data.fn_proto.return_type;
- if (!is_node_void(return_type_node)) {
- fprintf(ar->f, " -> ");
- render_node(ar, return_type_node);
- }
+ fprintf(ar->f, " -> ");
+ render_node(ar, return_type_node);
break;
}
case NodeTypeFnDef:
@@ -521,14 +508,7 @@ static void render_node(AstRender *ar, AstNode *node) {
break;
}
case NodeTypeSymbol:
- {
- TypeTableEntry *override_type = node->data.symbol_expr.override_type_entry;
- if (override_type) {
- fprintf(ar->f, "%s", buf_ptr(&override_type->name));
- } else {
- print_symbol(ar, node->data.symbol_expr.symbol);
- }
- }
+ print_symbol(ar, node->data.symbol_expr.symbol);
break;
case NodeTypePrefixOpExpr:
{
@@ -623,10 +603,8 @@ static void render_node(AstRender *ar, AstNode *node) {
assert(field_node->type == NodeTypeStructField);
print_indent(ar);
print_symbol(ar, field_node->data.struct_field.name);
- if (!is_node_void(field_node->data.struct_field.type)) {
- fprintf(ar->f, ": ");
- render_node(ar, field_node->data.struct_field.type);
- }
+ fprintf(ar->f, ": ");
+ render_node(ar, field_node->data.struct_field.type);
fprintf(ar->f, ",\n");
}
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 9f19279bfc..086394a950 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -2965,6 +2965,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdToPtrType:
case IrInstructionIdPtrTypeChild:
case IrInstructionIdFieldPtr:
+ case IrInstructionIdSetFnTest:
zig_unreachable();
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -2996,7 +2997,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
case IrInstructionIdSwitchBr:
case IrInstructionIdPhi:
- case IrInstructionIdBuiltinCall:
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:
case IrInstructionIdReadField:
@@ -5303,8 +5303,6 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
}
static void get_c_type_node(CodeGen *g, AstNode *type_node, Buf *out_buf) {
- assert(type_node->type != NodeTypeSymbol || !type_node->data.symbol_expr.override_type_entry);
-
Expr *expr = get_resolved_expr(type_node);
assert(expr->type_entry);
assert(expr->type_entry->id == TypeTableEntryIdMetaType);
diff --git a/src/ir.cpp b/src/ir.cpp
index 52a016185d..96bf78bdf5 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -149,10 +149,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCall *) {
return IrInstructionIdCall;
}
-static constexpr IrInstructionId ir_instruction_id(IrInstructionBuiltinCall *) {
- return IrInstructionIdBuiltinCall;
-}
-
static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) {
return IrInstructionIdConst;
}
@@ -189,6 +185,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *)
return IrInstructionIdPtrTypeChild;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) {
+ return IrInstructionIdSetFnTest;
+}
+
template<typename T>
static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@@ -353,7 +353,7 @@ static IrInstruction *ir_build_const_scope(IrBuilder *irb, AstNode *source_node,
static IrInstruction *ir_build_const_bool(IrBuilder *irb, AstNode *source_node, bool value) {
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
- const_instruction->base.type_entry = irb->codegen->builtin_types.entry_block;
+ const_instruction->base.type_entry = irb->codegen->builtin_types.entry_bool;
const_instruction->base.static_value.ok = true;
const_instruction->base.static_value.data.x_bool = value;
return &const_instruction->base;
@@ -505,20 +505,6 @@ static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_inst
return new_instruction;
}
-static IrInstruction *ir_build_builtin_call(IrBuilder *irb, AstNode *source_node,
- BuiltinFnEntry *fn, IrInstruction **args)
-{
- IrInstructionBuiltinCall *call_instruction = ir_build_instruction<IrInstructionBuiltinCall>(irb, source_node);
- call_instruction->fn = fn;
- call_instruction->args = args;
-
- for (size_t i = 0; i < fn->param_count; i += 1) {
- ir_ref_instruction(args[i]);
- }
-
- return &call_instruction->base;
-}
-
static IrInstruction *ir_build_phi(IrBuilder *irb, AstNode *source_node,
size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values)
{
@@ -717,6 +703,19 @@ static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, AstNode *source_no
return &instruction->base;
}
+static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, AstNode *source_node, IrInstruction *fn_value,
+ IrInstruction *is_test)
+{
+ IrInstructionSetFnTest *instruction = ir_build_instruction<IrInstructionSetFnTest>(irb, source_node);
+ instruction->fn_value = fn_value;
+ instruction->is_test = is_test;
+
+ ir_ref_instruction(fn_value);
+ ir_ref_instruction(is_test);
+
+ return &instruction->base;
+}
+
//static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
// size_t result = 0;
// while (inner_block != outer_block) {
@@ -1066,10 +1065,6 @@ static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstN
static IrInstruction *ir_gen_symbol(IrBuilder *irb, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypeSymbol);
- if (node->data.symbol_expr.override_type_entry) {
- zig_panic("TODO have parseh directly generate IR");
- }
-
Buf *variable_name = node->data.symbol_expr.symbol;
auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name);
@@ -1163,26 +1158,71 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
builtin_fn->ref_count += 1;
- if (builtin_fn->id == BuiltinFnIdUnreachable) {
- return ir_build_unreachable(irb, node);
- } else if (builtin_fn->id == BuiltinFnIdTypeof) {
- AstNode *arg_node = node->data.fn_call_expr.params.at(0);
- IrInstruction *arg = ir_gen_node(irb, arg_node, node->block_context);
- if (arg == irb->codegen->invalid_instruction)
- return arg;
- return ir_build_typeof(irb, node, arg);
- }
+ switch (builtin_fn->id) {
+ case BuiltinFnIdInvalid:
+ zig_unreachable();
+ case BuiltinFnIdUnreachable:
+ return ir_build_unreachable(irb, node);
+ case BuiltinFnIdTypeof:
+ {
+ AstNode *arg_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *arg = ir_gen_node(irb, arg_node, node->block_context);
+ if (arg == irb->codegen->invalid_instruction)
+ return arg;
+ return ir_build_typeof(irb, node, arg);
+ }
+ case BuiltinFnIdSetFnTest:
+ {
+ AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context);
+ if (arg0_value == irb->codegen->invalid_instruction)
+ return arg0_value;
- IrInstruction **args = allocate<IrInstruction *>(actual_param_count);
- for (size_t i = 0; i < actual_param_count; i += 1) {
- AstNode *arg_node = node->data.fn_call_expr.params.at(i);
- IrInstruction *arg = ir_gen_node(irb, arg_node, node->block_context);
- if (arg == irb->codegen->invalid_instruction)
- return arg;
- args[i] = arg;
- }
+ AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+ IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, node->block_context);
+ if (arg1_value == irb->codegen->invalid_instruction)
+ return arg1_value;
- return ir_build_builtin_call(irb, node, builtin_fn, args);
+ return ir_build_set_fn_test(irb, node, arg0_value, arg1_value);
+ }
+ case BuiltinFnIdMemcpy:
+ case BuiltinFnIdMemset:
+ case BuiltinFnIdSizeof:
+ case BuiltinFnIdAlignof:
+ case BuiltinFnIdMaxValue:
+ case BuiltinFnIdMinValue:
+ case BuiltinFnIdMemberCount:
+ case BuiltinFnIdAddWithOverflow:
+ case BuiltinFnIdSubWithOverflow:
+ case BuiltinFnIdMulWithOverflow:
+ case BuiltinFnIdShlWithOverflow:
+ case BuiltinFnIdCInclude:
+ case BuiltinFnIdCDefine:
+ case BuiltinFnIdCUndef:
+ case BuiltinFnIdCompileVar:
+ case BuiltinFnIdCompileErr:
+ case BuiltinFnIdConstEval:
+ case BuiltinFnIdCtz:
+ case BuiltinFnIdClz:
+ case BuiltinFnIdImport:
+ case BuiltinFnIdCImport:
+ case BuiltinFnIdErrName:
+ case BuiltinFnIdBreakpoint:
+ case BuiltinFnIdReturnAddress:
+ case BuiltinFnIdFrameAddress:
+ case BuiltinFnIdEmbedFile:
+ case BuiltinFnIdCmpExchange:
+ case BuiltinFnIdFence:
+ case BuiltinFnIdDivExact:
+ case BuiltinFnIdTruncate:
+ case BuiltinFnIdIntType:
+ case BuiltinFnIdSetFnVisible:
+ case BuiltinFnIdSetFnStaticEval:
+ case BuiltinFnIdSetFnNoInline:
+ case BuiltinFnIdSetDebugSafety:
+ zig_panic("TODO IR gen more builtin functions");
+ }
+ zig_unreachable();
}
static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) {
@@ -2044,6 +2084,53 @@ static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value
return const_val->data.x_type;
}
+static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out) {
+ if (bool_value == ira->codegen->invalid_instruction)
+ return false;
+
+ if (bool_value->type_entry->id == TypeTableEntryIdInvalid)
+ return false;
+
+ if (bool_value->type_entry->id != TypeTableEntryIdBool) {
+ add_node_error(ira->codegen, bool_value->source_node,
+ buf_sprintf("expected type 'bool', found '%s'", buf_ptr(&bool_value->type_entry->name)));
+ return false;
+ }
+
+ ConstExprValue *const_val = &bool_value->static_value;
+ if (!const_val->ok) {
+ add_node_error(ira->codegen, bool_value->source_node,
+ buf_sprintf("unable to evaluate constant expression"));
+ return false;
+ }
+
+ *out = const_val->data.x_bool;
+ return true;
+}
+
+static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
+ if (fn_value == ira->codegen->invalid_instruction)
+ return nullptr;
+
+ if (fn_value->type_entry->id == TypeTableEntryIdInvalid)
+ return nullptr;
+
+ if (fn_value->type_entry->id != TypeTableEntryIdFn) {
+ add_node_error(ira->codegen, fn_value->source_node,
+ buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->type_entry->name)));
+ return nullptr;
+ }
+
+ ConstExprValue *const_val = &fn_value->static_value;
+ if (!const_val->ok) {
+ add_node_error(ira->codegen, fn_value->source_node,
+ buf_sprintf("unable to evaluate constant expression"));
+ return nullptr;
+ }
+
+ return const_val->data.x_fn;
+}
+
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *dest_type, IrInstruction *value)
{
@@ -2837,11 +2924,18 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
} else if (fn_ref->type_entry->id == TypeTableEntryIdFn) {
// TODO fully port over the fn call analyze code to IR
FnTableEntry *fn_table_entry = fn_ref->static_value.data.x_fn;
+ TypeTableEntry *fn_type = fn_table_entry->type_entry;
+
+ IrInstruction **casted_args = allocate<IrInstruction *>(call_instruction->arg_count);
+ for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
+ TypeTableEntry *param_type = fn_type->data.fn.fn_type_id.param_info[i].type;
+ IrInstruction *old_arg = call_instruction->args[i]->other;
+ casted_args[i] = ir_get_casted_value(ira, old_arg, param_type);
+ }
ir_build_call_from(&ira->new_irb, &call_instruction->base,
- call_instruction->fn, call_instruction->arg_count, call_instruction->args);
+ call_instruction->fn, call_instruction->arg_count, casted_args);
- TypeTableEntry *fn_type = fn_table_entry->type_entry;
TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
return return_type;
} else {
@@ -3075,6 +3169,696 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
zig_unreachable();
}
+static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) {
+ IrBasicBlock *old_dest_block = br_instruction->dest_block;
+
+ // TODO detect backward jumps
+
+ if (br_instruction->is_inline || old_dest_block->ref_count == 1) {
+ ir_inline_bb(ira, old_dest_block);
+ return ira->codegen->builtin_types.entry_unreachable;
+ }
+
+ IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block);
+ ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb);
+ ir_finish_bb(ira);
+ return ira->codegen->builtin_types.entry_unreachable;
+}
+
+static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) {
+ TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
+ IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type);
+ if (condition == ira->codegen->invalid_instruction) {
+ ir_finish_bb(ira);
+ return ira->codegen->builtin_types.entry_unreachable;
+ }
+
+ // TODO detect backward jumps
+ if (condition->static_value.ok) {
+ IrBasicBlock *old_dest_block = condition->static_value.data.x_bool ?
+ cond_br_instruction->then_block : cond_br_instruction->else_block;
+
+ if (cond_br_instruction->is_inline || old_dest_block->ref_count == 1) {
+ ir_inline_bb(ira, old_dest_block);
+ return ira->codegen->builtin_types.entry_unreachable;
+ }
+ } else if (cond_br_instruction->is_inline) {
+ add_node_error(ira->codegen, condition->source_node,
+ buf_sprintf("unable to evaluate constant expression"));
+ ir_finish_bb(ira);
+ return ira->codegen->builtin_types.entry_unreachable;
+ }
+
+ IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block);
+ IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block);
+ ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, condition, new_then_block, new_else_block, false);
+ ir_finish_bb(ira);
+ return ira->codegen->builtin_types.entry_unreachable;
+}
+
+static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira,
+ IrInstructionUnreachable *unreachable_instruction)
+{
+ ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base);
+ ir_finish_bb(ira);
+ return ira->codegen->builtin_types.entry_unreachable;
+}
+
+static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) {
+ if (ira->const_predecessor_bb) {
+ for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
+ IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
+ if (predecessor != ira->const_predecessor_bb)
+ continue;
+ IrInstruction *value = phi_instruction->incoming_values[i]->other;
+ assert(value->type_entry);
+ if (value->static_value.ok) {
+ ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base,
+ value->static_value.depends_on_compile_var);
+ *out_val = value->static_value;
+ } else {
+ phi_instruction->base.other = value;
+ }
+ return value->type_entry;
+ }
+ zig_unreachable();
+ }
+
+ ZigList<IrBasicBlock*> new_incoming_blocks = {0};
+ ZigList<IrInstruction*> new_incoming_values = {0};
+
+ for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
+ IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
+ if (predecessor->ref_count == 0)
+ continue;
+
+ assert(predecessor->other);
+ new_incoming_blocks.append(predecessor->other);
+
+ IrInstruction *old_value = phi_instruction->incoming_values[i];
+ assert(old_value);
+ new_incoming_values.append(old_value->other);
+ }
+ assert(new_incoming_blocks.length != 0);
+
+ if (new_incoming_blocks.length == 1) {
+ IrInstruction *first_value = new_incoming_values.at(0);
+ phi_instruction->base.other = first_value;
+ return first_value->type_entry;
+ }
+
+ TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node,
+ new_incoming_values.items, new_incoming_values.length);
+ if (resolved_type->id == TypeTableEntryIdInvalid)
+ return resolved_type;
+
+ ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length,
+ new_incoming_blocks.items, new_incoming_values.items);
+ return resolved_type;
+}
+
+static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
+ VariableTableEntry *var = var_ptr_instruction->var;
+ if (var->type->id == TypeTableEntryIdInvalid)
+ return var->type;
+
+ TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var->type, false);
+ // TODO once the anlayze code is fully ported over to IR we won't need this SIZE_MAX thing.
+ if (var->mem_slot_index != SIZE_MAX) {
+ ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
+ if (mem_slot->ok) {
+ ConstExprValue *out_val = ir_build_const_from(ira, &var_ptr_instruction->base,
+ mem_slot->depends_on_compile_var);
+
+ out_val->data.x_ptr.len = 1;
+ out_val->data.x_ptr.is_c_str = false;
+ out_val->data.x_ptr.ptr = allocate<ConstExprValue *>(1);
+ out_val->data.x_ptr.ptr[0] = mem_slot;
+ return ptr_type;
+ }
+ }
+
+ ir_build_var_ptr_from(&ira->new_irb, &var_ptr_instruction->base, var);
+ return ptr_type;
+}
+
+static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
+ IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;
+ IrInstruction *elem_index = elem_ptr_instruction->elem_index->other;
+
+ TypeTableEntry *array_type = array_ptr->type_entry;
+ TypeTableEntry *return_type;
+
+ if (array_type->id == TypeTableEntryIdInvalid) {
+ return array_type;
+ } else if (array_type->id == TypeTableEntryIdArray) {
+ if (array_type->data.array.len == 0) {
+ add_node_error(ira->codegen, elem_ptr_instruction->base.source_node,
+ buf_sprintf("out of bounds array access"));
+ }
+ TypeTableEntry *child_type = array_type->data.array.child_type;
+ return_type = get_pointer_to_type(ira->codegen, child_type, false);
+ } else if (array_type->id == TypeTableEntryIdPointer) {
+ return_type = array_type;
+ } else if (is_slice(array_type)) {
+ return_type = array_type->data.structure.fields[0].type_entry;
+ } else {
+ add_node_error(ira->codegen, elem_ptr_instruction->base.source_node,
+ buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
+ IrInstruction *casted_elem_index = ir_get_casted_value(ira, elem_index, usize);
+ if (casted_elem_index == ira->codegen->invalid_instruction)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (array_ptr->static_value.ok && casted_elem_index->static_value.ok) {
+ zig_panic("TODO compile time array access");
+ }
+
+ ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, casted_elem_index);
+
+ return return_type;
+}
+
+static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
+ TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction,
+ TypeTableEntry *container_type)
+{
+ if (!is_slice(bare_struct_type)) {
+ BlockContext *container_block_context = get_container_block_context(bare_struct_type);
+ assert(container_block_context);
+ auto entry = container_block_context->decl_table.maybe_get(field_name);
+ AstNode *fn_decl_node = entry ? entry->value : nullptr;
+ if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
+ zig_panic("TODO member function call");
+ }
+ }
+ add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
+ buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+}
+
+
+static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *field_name,
+ IrInstructionFieldPtr *field_ptr_instruction, TypeTableEntry *container_type)
+{
+ IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
+ TypeTableEntry *bare_type = container_ref_type(container_type);
+ if (!type_is_complete(bare_type)) {
+ resolve_container_type(ira->codegen, bare_type);
+ }
+
+ if (bare_type->id == TypeTableEntryIdStruct) {
+ TypeStructField *field = find_struct_type_field(bare_type, field_name);
+ if (field) {
+ ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
+ return get_pointer_to_type(ira->codegen, field->type_entry, false);
+ } else {
+ return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
+ field_ptr_instruction, container_type);
+ }
+ } else if (bare_type->id == TypeTableEntryIdEnum) {
+ zig_panic("TODO enum field ptr");
+ } else if (bare_type->id == TypeTableEntryIdUnion) {
+ zig_panic("TODO");
+ } else {
+ zig_unreachable();
+ }
+}
+
+static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
+ IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
+ Buf *field_name = field_ptr_instruction->field_name;
+
+ TypeTableEntry *container_type = container_ptr->type_entry;
+ if (container_type->id == TypeTableEntryIdInvalid) {
+ return container_type;
+ } else if (is_container_ref(container_type)) {
+ return ir_analyze_container_member_access(ira, field_name, field_ptr_instruction, container_type);
+ } else if (container_type->id == TypeTableEntryIdArray) {
+ if (buf_eql_str(field_name, "len")) {
+ add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
+ buf_sprintf("pointer to array length not available"));
+ return ira->codegen->builtin_types.entry_invalid;
+ } else {
+ add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
+ buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
+ buf_ptr(&container_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ } else if (container_type->id == TypeTableEntryIdMetaType) {
+ TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr);
+
+ if (child_type->id == TypeTableEntryIdInvalid) {
+ return ira->codegen->builtin_types.entry_invalid;
+ } else if (child_type->id == TypeTableEntryIdEnum) {
+ zig_panic("TODO enum type field");
+ } else if (child_type->id == TypeTableEntryIdStruct) {
+ zig_panic("TODO struct type field");
+ } else if (child_type->id == TypeTableEntryIdPureError) {
+ zig_panic("TODO error type field");
+ } else if (child_type->id == TypeTableEntryIdInt) {
+ zig_panic("TODO integer type field");
+ } else {
+ add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
+ buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ } else if (container_type->id == TypeTableEntryIdNamespace) {
+ zig_panic("TODO namespace field access");
+ } else {
+ add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
+ buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+}
+
+static TypeTableEntry *ir_analyze_read_field_as_ptr_load(IrAnalyze *ira,
+ IrInstructionReadField *read_field_instruction)
+{
+ IrInstruction *old_field_ptr_inst = ir_build_field_ptr(&ira->old_irb, read_field_instruction->base.source_node,
+ read_field_instruction->container_ptr, read_field_instruction->field_name);
+ IrInstruction *old_load_ptr_inst = ir_build_load_ptr(&ira->old_irb, read_field_instruction->base.source_node,
+ old_field_ptr_inst);
+ ir_analyze_instruction(ira, old_field_ptr_inst);
+ TypeTableEntry *result_type = ir_analyze_instruction(ira, old_load_ptr_inst);
+ read_field_instruction->base.other = old_load_ptr_inst->other;
+ return result_type;
+}
+
+static TypeTableEntry *ir_analyze_instruction_read_field(IrAnalyze *ira,
+ IrInstructionReadField *read_field_instruction)
+{
+ IrInstruction *container_ptr = read_field_instruction->container_ptr->other;
+ Buf *field_name = read_field_instruction->field_name;
+
+ TypeTableEntry *container_type = container_ptr->type_entry;
+ if (container_type->id == TypeTableEntryIdInvalid) {
+ return container_type;
+ } else if (is_container_ref(container_type)) {
+ return ir_analyze_read_field_as_ptr_load(ira, read_field_instruction);
+ } else if (container_type->id == TypeTableEntryIdArray) {
+ if (buf_eql_str(field_name, "len")) {
+ return ir_analyze_const_usize(ira, &read_field_instruction->base, container_type->data.array.len, false);
+ } else {
+ add_node_error(ira->codegen, read_field_instruction->base.source_node,
+ buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
+ buf_ptr(&container_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ } else if (container_type->id == TypeTableEntryIdMetaType) {
+ TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr);
+
+ if (child_type->id == TypeTableEntryIdInvalid) {
+ return ira->codegen->builtin_types.entry_invalid;
+ } else if (child_type->id == TypeTableEntryIdEnum) {
+ zig_panic("TODO enum type field");
+ } else if (child_type->id == TypeTableEntryIdStruct) {
+ zig_panic("TODO struct type field");
+ } else if (child_type->id == TypeTableEntryIdPureError) {
+ zig_panic("TODO error type field");
+ } else if (child_type->id == TypeTableEntryIdInt) {
+ zig_panic("TODO integer type field");
+ } else {
+ add_node_error(ira->codegen, read_field_instruction->base.source_node,
+ buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ } else if (container_type->id == TypeTableEntryIdNamespace) {
+ zig_panic("TODO namespace field access");
+ } else {
+ add_node_error(ira->codegen, read_field_instruction->base.source_node,
+ buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+}
+
+static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
+ IrInstruction *ptr = load_ptr_instruction->ptr->other;
+ TypeTableEntry *type_entry = ptr->type_entry;
+ if (type_entry->id == TypeTableEntryIdInvalid) {
+ return type_entry;
+ } else if (type_entry->id == TypeTableEntryIdPointer) {
+ TypeTableEntry *child_type = type_entry->data.pointer.child_type;
+ if (ptr->static_value.ok) {
+ ConstExprValue *pointee = ptr->static_value.data.x_ptr.ptr[0];
+ if (pointee->ok) {
+ ConstExprValue *out_val = ir_build_const_from(ira, &load_ptr_instruction->base,
+ pointee->depends_on_compile_var);
+ *out_val = *pointee;
+ return child_type;
+ }
+ }
+ ir_build_load_ptr_from(&ira->new_irb, &load_ptr_instruction->base, ptr);
+ return child_type;
+ } else {
+ add_node_error(ira->codegen, load_ptr_instruction->base.source_node,
+ buf_sprintf("indirection requires pointer operand ('%s' invalid)",
+ buf_ptr(&type_entry->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+}
+
+static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) {
+ IrInstruction *ptr = store_ptr_instruction->ptr->other;
+ if (ptr->type_entry->id == TypeTableEntryIdInvalid)
+ return ptr->type_entry;
+
+ IrInstruction *value = store_ptr_instruction->value->other;
+ if (value->type_entry->id == TypeTableEntryIdInvalid)
+ return value->type_entry;
+
+ TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type;
+ IrInstruction *casted_value = ir_get_casted_value(ira, value, child_type);
+ if (casted_value == ira->codegen->invalid_instruction)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (ptr->static_value.ok && casted_value->static_value.ok) {
+ ConstExprValue *dest_val = ptr->static_value.data.x_ptr.ptr[0];
+ if (dest_val->ok) {
+ *dest_val = casted_value->static_value;
+ return ir_analyze_void(ira, &store_ptr_instruction->base);
+ }
+ }
+
+ if (ptr->static_value.ok) {
+ // This memory location is transforming from known at compile time to known at runtime.
+ // We must emit our own var ptr instruction.
+ ptr->static_value.ok = false;
+ IrInstruction *new_ptr_inst;
+ if (ptr->id == IrInstructionIdVarPtr) {
+ IrInstructionVarPtr *var_ptr_inst = (IrInstructionVarPtr *)ptr;
+ VariableTableEntry *var = var_ptr_inst->var;
+ new_ptr_inst = ir_build_var_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, var);
+ assert(var->mem_slot_index != SIZE_MAX);
+ ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
+ mem_slot->ok = false;
+ } else if (ptr->id == IrInstructionIdFieldPtr) {
+ zig_panic("TODO");
+ } else if (ptr->id == IrInstructionIdElemPtr) {
+ zig_panic("TODO");
+ } else {
+ zig_unreachable();
+ }
+ new_ptr_inst->type_entry = ptr->type_entry;
+ ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, new_ptr_inst, casted_value);
+ return ir_analyze_void(ira, &store_ptr_instruction->base);
+ }
+
+ ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value);
+ return ira->codegen->builtin_types.entry_void;
+}
+
+static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) {
+ IrInstruction *expr_value = typeof_instruction->value->other;
+ TypeTableEntry *type_entry = expr_value->type_entry;
+ switch (type_entry->id) {
+ case TypeTableEntryIdInvalid:
+ return type_entry;
+ case TypeTableEntryIdVar:
+ add_node_error(ira->codegen, expr_value->source_node,
+ buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ case TypeTableEntryIdNumLitFloat:
+ case TypeTableEntryIdNumLitInt:
+ case TypeTableEntryIdUndefLit:
+ case TypeTableEntryIdNullLit:
+ case TypeTableEntryIdNamespace:
+ case TypeTableEntryIdBlock:
+ case TypeTableEntryIdGenericFn:
+ case TypeTableEntryIdMetaType:
+ case TypeTableEntryIdVoid:
+ case TypeTableEntryIdBool:
+ case TypeTableEntryIdUnreachable:
+ case TypeTableEntryIdInt:
+ case TypeTableEntryIdFloat:
+ case TypeTableEntryIdPointer:
+ case TypeTableEntryIdArray:
+ case TypeTableEntryIdStruct:
+ case TypeTableEntryIdMaybe:
+ case TypeTableEntryIdErrorUnion:
+ case TypeTableEntryIdPureError:
+ case TypeTableEntryIdEnum:
+ case TypeTableEntryIdUnion:
+ case TypeTableEntryIdFn:
+ case TypeTableEntryIdTypeDecl:
+ {
+ ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false);
+ // TODO depends_on_compile_var should be set based on whether the type of the expression
+ // depends_on_compile_var. but we currently don't have a thing to tell us if the type of
+ // something depends on a compile var
+ out_val->data.x_type = type_entry;
+
+ return ira->codegen->builtin_types.entry_type;
+ }
+ }
+
+ zig_unreachable();
+}
+
+static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
+ IrInstructionToPtrType *to_ptr_type_instruction)
+{
+ IrInstruction *type_value = to_ptr_type_instruction->value->other;
+ TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
+ if (type_entry->id == TypeTableEntryIdInvalid)
+ return type_entry;
+
+ TypeTableEntry *ptr_type;
+ if (type_entry->id == TypeTableEntryIdArray) {
+ ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false);
+ } else if (is_slice(type_entry)) {
+ ptr_type = type_entry->data.structure.fields[0].type_entry;
+ } else {
+ add_node_error(ira->codegen, to_ptr_type_instruction->base.source_node,
+ buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &to_ptr_type_instruction->base,
+ type_value->static_value.depends_on_compile_var);
+ out_val->data.x_type = ptr_type;
+ return ira->codegen->builtin_types.entry_type;
+}
+
+static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
+ IrInstructionPtrTypeChild *ptr_type_child_instruction)
+{
+ IrInstruction *type_value = ptr_type_child_instruction->value->other;
+ TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
+ if (type_entry->id == TypeTableEntryIdInvalid)
+ return type_entry;
+
+ if (type_entry->id != TypeTableEntryIdPointer) {
+ add_node_error(ira->codegen, ptr_type_child_instruction->base.source_node,
+ buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &ptr_type_child_instruction->base,
+ type_value->static_value.depends_on_compile_var);
+ out_val->data.x_type = type_entry->data.pointer.child_type;
+ return ira->codegen->builtin_types.entry_type;
+}
+
+static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira,
+ IrInstructionSetFnTest *set_fn_test_instruction)
+{
+ IrInstruction *fn_value = set_fn_test_instruction->fn_value->other;
+ IrInstruction *is_test_value = set_fn_test_instruction->is_test->other;
+
+ FnTableEntry *fn_entry = ir_resolve_fn(ira, fn_value);
+ if (!fn_entry)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (!ir_resolve_bool(ira, is_test_value, &fn_entry->is_test))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ AstNode *source_node = set_fn_test_instruction->base.source_node;
+ if (fn_entry->fn_test_set_node) {
+ ErrorMsg *msg = add_node_error(ira->codegen, source_node,
+ buf_sprintf("function test attribute set twice"));
+ add_error_note(ira->codegen, msg, fn_entry->fn_test_set_node, buf_sprintf("first set here"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ fn_entry->fn_test_set_node = source_node;
+
+ ira->codegen->test_fn_count += 1;
+
+ ir_build_const_from(ira, &set_fn_test_instruction->base, false);
+ return ira->codegen->builtin_types.entry_void;
+}
+
+static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
+ switch (instruction->id) {
+ case IrInstructionIdInvalid:
+ zig_unreachable();
+ case IrInstructionIdReturn:
+ return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
+ case IrInstructionIdConst:
+ return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction);
+ case IrInstructionIdUnOp:
+ return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction);
+ case IrInstructionIdBinOp:
+ return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
+ case IrInstructionIdDeclVar:
+ return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction);
+ case IrInstructionIdLoadPtr:
+ return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction);
+ case IrInstructionIdStorePtr:
+ return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction);
+ case IrInstructionIdElemPtr:
+ return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction);
+ case IrInstructionIdVarPtr:
+ return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
+ case IrInstructionIdFieldPtr:
+ return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction);
+ case IrInstructionIdReadField:
+ return ir_analyze_instruction_read_field(ira, (IrInstructionReadField *)instruction);
+ case IrInstructionIdCall:
+ return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
+ case IrInstructionIdBr:
+ return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction);
+ case IrInstructionIdCondBr:
+ return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction);
+ case IrInstructionIdUnreachable:
+ return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction);
+ case IrInstructionIdPhi:
+ return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction);
+ case IrInstructionIdTypeOf:
+ return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction);
+ case IrInstructionIdToPtrType:
+ return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);
+ case IrInstructionIdPtrTypeChild:
+ return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
+ case IrInstructionIdSetFnTest:
+ return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction);
+ case IrInstructionIdSwitchBr:
+ case IrInstructionIdCast:
+ case IrInstructionIdContainerInitList:
+ case IrInstructionIdContainerInitFields:
+ case IrInstructionIdStructFieldPtr:
+ zig_panic("TODO analyze more instructions");
+ }
+ zig_unreachable();
+}
+
+static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {
+ TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);
+ instruction->type_entry = instruction_type;
+ if (instruction->other) {
+ instruction->other->type_entry = instruction_type;
+ } else {
+ assert(instruction_type->id == TypeTableEntryIdInvalid ||
+ instruction_type->id == TypeTableEntryIdUnreachable);
+ instruction->other = instruction;
+ }
+ return instruction_type;
+}
+
+// This function attempts to evaluate IR code while doing type checking and other analysis.
+// It emits a new IrExecutable which is partially evaluated IR code.
+TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec,
+ TypeTableEntry *expected_type, AstNode *expected_type_source_node)
+{
+ IrAnalyze ir_analyze_data = {};
+ IrAnalyze *ira = &ir_analyze_data;
+ ira->codegen = codegen;
+ ira->explicit_return_type = expected_type;
+
+ ira->old_irb.codegen = codegen;
+ ira->old_irb.exec = old_exec;
+
+ ira->new_irb.codegen = codegen;
+ ira->new_irb.exec = new_exec;
+
+ ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count;
+ ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count);
+
+ IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);
+ IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb);
+ ir_ref_bb(new_entry_bb);
+ ira->old_irb.current_basic_block = old_entry_bb;
+ ira->new_irb.current_basic_block = new_entry_bb;
+ ira->block_queue_index = 0;
+ ira->instruction_index = 0;
+
+ while (ira->block_queue_index < ira->old_bb_queue.length) {
+ IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index);
+
+ if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) {
+ ira->instruction_index += 1;
+ continue;
+ }
+
+ TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction);
+
+ // unreachable instructions do their own control flow.
+ if (return_type->id == TypeTableEntryIdUnreachable)
+ continue;
+
+ ira->instruction_index += 1;
+ }
+
+ return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items,
+ ira->implicit_return_type_list.length);
+}
+
+bool ir_has_side_effects(IrInstruction *instruction) {
+ switch (instruction->id) {
+ case IrInstructionIdInvalid:
+ zig_unreachable();
+ case IrInstructionIdBr:
+ case IrInstructionIdCondBr:
+ case IrInstructionIdSwitchBr:
+ case IrInstructionIdDeclVar:
+ case IrInstructionIdStorePtr:
+ case IrInstructionIdCall:
+ case IrInstructionIdReturn:
+ case IrInstructionIdUnreachable:
+ case IrInstructionIdSetFnTest:
+ return true;
+ case IrInstructionIdPhi:
+ case IrInstructionIdUnOp:
+ case IrInstructionIdBinOp:
+ case IrInstructionIdLoadPtr:
+ case IrInstructionIdConst:
+ case IrInstructionIdCast:
+ case IrInstructionIdContainerInitList:
+ case IrInstructionIdContainerInitFields:
+ case IrInstructionIdFieldPtr:
+ case IrInstructionIdElemPtr:
+ case IrInstructionIdVarPtr:
+ case IrInstructionIdTypeOf:
+ case IrInstructionIdToPtrType:
+ case IrInstructionIdPtrTypeChild:
+ case IrInstructionIdReadField:
+ case IrInstructionIdStructFieldPtr:
+ return false;
+ }
+ zig_unreachable();
+}
+
+IrInstruction *ir_exec_const_result(IrExecutable *exec) {
+ if (exec->basic_block_list.length != 1)
+ return nullptr;
+
+ IrBasicBlock *bb = exec->basic_block_list.at(0);
+ if (bb->instruction_list.length != 1)
+ return nullptr;
+
+ IrInstruction *only_inst = bb->instruction_list.at(0);
+ if (only_inst->id != IrInstructionIdReturn)
+ return nullptr;
+
+ IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst;
+ IrInstruction *value = ret_inst->value;
+ assert(value->static_value.ok);
+ return value;
+}
+
//static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
// AstNode *node, const char *err_format, bool is_max)
//{
@@ -3510,33 +4294,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
//
//}
//
-//static TypeTableEntry *analyze_set_fn_test(CodeGen *g, ImportTableEntry *import,
-// BlockContext *context, AstNode *node)
-//{
-// AstNode **fn_node = &node->data.fn_call_expr.params.at(0);
-// AstNode **value_node = &node->data.fn_call_expr.params.at(1);
-//
-// FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node);
-// if (!fn_entry) {
-// return g->builtin_types.entry_invalid;
-// }
-//
-// bool ok = resolve_const_expr_bool(g, import, context, value_node, &fn_entry->is_test);
-// if (!ok) {
-// return g->builtin_types.entry_invalid;
-// }
-//
-// if (fn_entry->fn_test_set_node) {
-// ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function test attribute set twice"));
-// add_error_note(g, msg, fn_entry->fn_test_set_node, buf_sprintf("first set here"));
-// return g->builtin_types.entry_invalid;
-// }
-// fn_entry->fn_test_set_node = node;
-//
-// g->test_fn_count += 1;
-// return g->builtin_types.entry_void;
-//}
-//
//static TypeTableEntry *analyze_set_fn_no_inline(CodeGen *g, ImportTableEntry *import,
// BlockContext *context, AstNode *node)
//{
@@ -4188,762 +4945,2276 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
// }
//}
-static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) {
- IrBasicBlock *old_dest_block = br_instruction->dest_block;
-
- // TODO detect backward jumps
-
- if (br_instruction->is_inline || old_dest_block->ref_count == 1) {
- ir_inline_bb(ira, old_dest_block);
- return ira->codegen->builtin_types.entry_unreachable;
- }
-
- IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block);
- ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb);
- ir_finish_bb(ira);
- return ira->codegen->builtin_types.entry_unreachable;
-}
-
-static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) {
- TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
- IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type);
- if (condition == ira->codegen->invalid_instruction) {
- ir_finish_bb(ira);
- return ira->codegen->builtin_types.entry_unreachable;
- }
-
- // TODO detect backward jumps
- if (condition->static_value.ok) {
- IrBasicBlock *old_dest_block = condition->static_value.data.x_bool ?
- cond_br_instruction->then_block : cond_br_instruction->else_block;
-
- if (cond_br_instruction->is_inline || old_dest_block->ref_count == 1) {
- ir_inline_bb(ira, old_dest_block);
- return ira->codegen->builtin_types.entry_unreachable;
- }
- } else if (cond_br_instruction->is_inline) {
- add_node_error(ira->codegen, condition->source_node,
- buf_sprintf("unable to evaluate constant expression"));
- ir_finish_bb(ira);
- return ira->codegen->builtin_types.entry_unreachable;
- }
-
- IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block);
- IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block);
- ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, condition, new_then_block, new_else_block, false);
- ir_finish_bb(ira);
- return ira->codegen->builtin_types.entry_unreachable;
-}
-
-static TypeTableEntry *ir_analyze_instruction_builtin_call(IrAnalyze *ira,
- IrInstructionBuiltinCall *builtin_call_instruction)
-{
- switch (builtin_call_instruction->fn->id) {
- case BuiltinFnIdInvalid:
- case BuiltinFnIdUnreachable:
- case BuiltinFnIdTypeof:
- zig_unreachable();
- case BuiltinFnIdMemcpy:
- case BuiltinFnIdMemset:
- case BuiltinFnIdSizeof:
- case BuiltinFnIdAlignof:
- case BuiltinFnIdMaxValue:
- case BuiltinFnIdMinValue:
- case BuiltinFnIdMemberCount:
- case BuiltinFnIdAddWithOverflow:
- case BuiltinFnIdSubWithOverflow:
- case BuiltinFnIdMulWithOverflow:
- case BuiltinFnIdShlWithOverflow:
- case BuiltinFnIdCInclude:
- case BuiltinFnIdCDefine:
- case BuiltinFnIdCUndef:
- case BuiltinFnIdCompileVar:
- case BuiltinFnIdCompileErr:
- case BuiltinFnIdConstEval:
- case BuiltinFnIdCtz:
- case BuiltinFnIdClz:
- case BuiltinFnIdImport:
- case BuiltinFnIdCImport:
- case BuiltinFnIdErrName:
- case BuiltinFnIdBreakpoint:
- case BuiltinFnIdReturnAddress:
- case BuiltinFnIdFrameAddress:
- case BuiltinFnIdEmbedFile:
- case BuiltinFnIdCmpExchange:
- case BuiltinFnIdFence:
- case BuiltinFnIdDivExact:
- case BuiltinFnIdTruncate:
- case BuiltinFnIdIntType:
- case BuiltinFnIdSetFnTest:
- case BuiltinFnIdSetFnVisible:
- case BuiltinFnIdSetFnStaticEval:
- case BuiltinFnIdSetFnNoInline:
- case BuiltinFnIdSetDebugSafety:
- zig_panic("TODO analyze more builtin functions");
- }
- zig_unreachable();
-}
-
-static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira,
- IrInstructionUnreachable *unreachable_instruction)
-{
- ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base);
- ir_finish_bb(ira);
- return ira->codegen->builtin_types.entry_unreachable;
-}
-
-static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) {
- if (ira->const_predecessor_bb) {
- for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
- IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
- if (predecessor != ira->const_predecessor_bb)
- continue;
- IrInstruction *value = phi_instruction->incoming_values[i]->other;
- assert(value->type_entry);
- if (value->static_value.ok) {
- ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base,
- value->static_value.depends_on_compile_var);
- *out_val = value->static_value;
- } else {
- phi_instruction->base.other = value;
- }
- return value->type_entry;
- }
- zig_unreachable();
- }
-
- ZigList<IrBasicBlock*> new_incoming_blocks = {0};
- ZigList<IrInstruction*> new_incoming_values = {0};
-
- for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
- IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
- if (predecessor->ref_count == 0)
- continue;
-
- assert(predecessor->other);
- new_incoming_blocks.append(predecessor->other);
-
- IrInstruction *old_value = phi_instruction->incoming_values[i];
- assert(old_value);
- new_incoming_values.append(old_value->other);
- }
- assert(new_incoming_blocks.length != 0);
-
- if (new_incoming_blocks.length == 1) {
- IrInstruction *first_value = new_incoming_values.at(0);
- phi_instruction->base.other = first_value;
- return first_value->type_entry;
- }
-
- TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node,
- new_incoming_values.items, new_incoming_values.length);
- if (resolved_type->id == TypeTableEntryIdInvalid)
- return resolved_type;
-
- ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length,
- new_incoming_blocks.items, new_incoming_values.items);
- return resolved_type;
-}
-
-static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) {
- VariableTableEntry *var = var_ptr_instruction->var;
- if (var->type->id == TypeTableEntryIdInvalid)
- return var->type;
-
- TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var->type, false);
- // TODO once the anlayze code is fully ported over to IR we won't need this SIZE_MAX thing.
- if (var->mem_slot_index != SIZE_MAX) {
- ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
- if (mem_slot->ok) {
- ConstExprValue *out_val = ir_build_const_from(ira, &var_ptr_instruction->base,
- mem_slot->depends_on_compile_var);
-
- out_val->data.x_ptr.len = 1;
- out_val->data.x_ptr.is_c_str = false;
- out_val->data.x_ptr.ptr = allocate<ConstExprValue *>(1);
- out_val->data.x_ptr.ptr[0] = mem_slot;
- return ptr_type;
- }
- }
-
- ir_build_var_ptr_from(&ira->new_irb, &var_ptr_instruction->base, var);
- return ptr_type;
-}
-
-static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
- IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;
- IrInstruction *elem_index = elem_ptr_instruction->elem_index->other;
-
- TypeTableEntry *array_type = array_ptr->type_entry;
- TypeTableEntry *return_type;
-
- if (array_type->id == TypeTableEntryIdInvalid) {
- return array_type;
- } else if (array_type->id == TypeTableEntryIdArray) {
- if (array_type->data.array.len == 0) {
- add_node_error(ira->codegen, elem_ptr_instruction->base.source_node,
- buf_sprintf("out of bounds array access"));
- }
- TypeTableEntry *child_type = array_type->data.array.child_type;
- return_type = get_pointer_to_type(ira->codegen, child_type, false);
- } else if (array_type->id == TypeTableEntryIdPointer) {
- return_type = array_type;
- } else if (is_slice(array_type)) {
- return_type = array_type->data.structure.fields[0].type_entry;
- } else {
- add_node_error(ira->codegen, elem_ptr_instruction->base.source_node,
- buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
-
- TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
- IrInstruction *casted_elem_index = ir_get_casted_value(ira, elem_index, usize);
- if (casted_elem_index == ira->codegen->invalid_instruction)
- return ira->codegen->builtin_types.entry_invalid;
-
- if (array_ptr->static_value.ok && casted_elem_index->static_value.ok) {
- zig_panic("TODO compile time array access");
- }
-
- ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, casted_elem_index);
-
- return return_type;
-}
-
-static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
- TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction,
- TypeTableEntry *container_type)
-{
- if (!is_slice(bare_struct_type)) {
- BlockContext *container_block_context = get_container_block_context(bare_struct_type);
- assert(container_block_context);
- auto entry = container_block_context->decl_table.maybe_get(field_name);
- AstNode *fn_decl_node = entry ? entry->value : nullptr;
- if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
- zig_panic("TODO member function call");
- }
- }
- add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
-}
-
-
-static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *field_name,
- IrInstructionFieldPtr *field_ptr_instruction, TypeTableEntry *container_type)
-{
- IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
- TypeTableEntry *bare_type = container_ref_type(container_type);
- if (!type_is_complete(bare_type)) {
- resolve_container_type(ira->codegen, bare_type);
- }
-
- if (bare_type->id == TypeTableEntryIdStruct) {
- TypeStructField *field = find_struct_type_field(bare_type, field_name);
- if (field) {
- ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
- return get_pointer_to_type(ira->codegen, field->type_entry, false);
- } else {
- return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
- field_ptr_instruction, container_type);
- }
- } else if (bare_type->id == TypeTableEntryIdEnum) {
- zig_panic("TODO enum field ptr");
- } else if (bare_type->id == TypeTableEntryIdUnion) {
- zig_panic("TODO");
- } else {
- zig_unreachable();
- }
-}
-
-static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
- IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
- Buf *field_name = field_ptr_instruction->field_name;
-
- TypeTableEntry *container_type = container_ptr->type_entry;
- if (container_type->id == TypeTableEntryIdInvalid) {
- return container_type;
- } else if (is_container_ref(container_type)) {
- return ir_analyze_container_member_access(ira, field_name, field_ptr_instruction, container_type);
- } else if (container_type->id == TypeTableEntryIdArray) {
- if (buf_eql_str(field_name, "len")) {
- add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
- buf_sprintf("pointer to array length not available"));
- return ira->codegen->builtin_types.entry_invalid;
- } else {
- add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
- buf_ptr(&container_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
- } else if (container_type->id == TypeTableEntryIdMetaType) {
- TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr);
-
- if (child_type->id == TypeTableEntryIdInvalid) {
- return ira->codegen->builtin_types.entry_invalid;
- } else if (child_type->id == TypeTableEntryIdEnum) {
- zig_panic("TODO enum type field");
- } else if (child_type->id == TypeTableEntryIdStruct) {
- zig_panic("TODO struct type field");
- } else if (child_type->id == TypeTableEntryIdPureError) {
- zig_panic("TODO error type field");
- } else if (child_type->id == TypeTableEntryIdInt) {
- zig_panic("TODO integer type field");
- } else {
- add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
- buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
- } else if (container_type->id == TypeTableEntryIdNamespace) {
- zig_panic("TODO namespace field access");
- } else {
- add_node_error(ira->codegen, field_ptr_instruction->base.source_node,
- buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
-}
-
-static TypeTableEntry *ir_analyze_read_field_as_ptr_load(IrAnalyze *ira,
- IrInstructionReadField *read_field_instruction)
-{
- IrInstruction *old_field_ptr_inst = ir_build_field_ptr(&ira->old_irb, read_field_instruction->base.source_node,
- read_field_instruction->container_ptr, read_field_instruction->field_name);
- IrInstruction *old_load_ptr_inst = ir_build_load_ptr(&ira->old_irb, read_field_instruction->base.source_node,
- old_field_ptr_inst);
- ir_analyze_instruction(ira, old_field_ptr_inst);
- TypeTableEntry *result_type = ir_analyze_instruction(ira, old_load_ptr_inst);
- read_field_instruction->base.other = old_load_ptr_inst->other;
- return result_type;
-}
-
-static TypeTableEntry *ir_analyze_instruction_read_field(IrAnalyze *ira,
- IrInstructionReadField *read_field_instruction)
-{
- IrInstruction *container_ptr = read_field_instruction->container_ptr->other;
- Buf *field_name = read_field_instruction->field_name;
-
- TypeTableEntry *container_type = container_ptr->type_entry;
- if (container_type->id == TypeTableEntryIdInvalid) {
- return container_type;
- } else if (is_container_ref(container_type)) {
- return ir_analyze_read_field_as_ptr_load(ira, read_field_instruction);
- } else if (container_type->id == TypeTableEntryIdArray) {
- if (buf_eql_str(field_name, "len")) {
- return ir_analyze_const_usize(ira, &read_field_instruction->base, container_type->data.array.len, false);
- } else {
- add_node_error(ira->codegen, read_field_instruction->base.source_node,
- buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
- buf_ptr(&container_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
- } else if (container_type->id == TypeTableEntryIdMetaType) {
- TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr);
-
- if (child_type->id == TypeTableEntryIdInvalid) {
- return ira->codegen->builtin_types.entry_invalid;
- } else if (child_type->id == TypeTableEntryIdEnum) {
- zig_panic("TODO enum type field");
- } else if (child_type->id == TypeTableEntryIdStruct) {
- zig_panic("TODO struct type field");
- } else if (child_type->id == TypeTableEntryIdPureError) {
- zig_panic("TODO error type field");
- } else if (child_type->id == TypeTableEntryIdInt) {
- zig_panic("TODO integer type field");
- } else {
- add_node_error(ira->codegen, read_field_instruction->base.source_node,
- buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
- } else if (container_type->id == TypeTableEntryIdNamespace) {
- zig_panic("TODO namespace field access");
- } else {
- add_node_error(ira->codegen, read_field_instruction->base.source_node,
- buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
-}
-
-static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
- IrInstruction *ptr = load_ptr_instruction->ptr->other;
- TypeTableEntry *type_entry = ptr->type_entry;
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdPointer) {
- TypeTableEntry *child_type = type_entry->data.pointer.child_type;
- if (ptr->static_value.ok) {
- ConstExprValue *pointee = ptr->static_value.data.x_ptr.ptr[0];
- if (pointee->ok) {
- ConstExprValue *out_val = ir_build_const_from(ira, &load_ptr_instruction->base,
- pointee->depends_on_compile_var);
- *out_val = *pointee;
- return child_type;
- }
- }
- ir_build_load_ptr_from(&ira->new_irb, &load_ptr_instruction->base, ptr);
- return child_type;
- } else {
- add_node_error(ira->codegen, load_ptr_instruction->base.source_node,
- buf_sprintf("indirection requires pointer operand ('%s' invalid)",
- buf_ptr(&type_entry->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
-}
-
-static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) {
- IrInstruction *ptr = store_ptr_instruction->ptr->other;
- if (ptr->type_entry->id == TypeTableEntryIdInvalid)
- return ptr->type_entry;
-
- IrInstruction *value = store_ptr_instruction->value->other;
- if (value->type_entry->id == TypeTableEntryIdInvalid)
- return value->type_entry;
-
- TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type;
- IrInstruction *casted_value = ir_get_casted_value(ira, value, child_type);
- if (casted_value == ira->codegen->invalid_instruction)
- return ira->codegen->builtin_types.entry_invalid;
-
- if (ptr->static_value.ok && casted_value->static_value.ok) {
- ConstExprValue *dest_val = ptr->static_value.data.x_ptr.ptr[0];
- if (dest_val->ok) {
- *dest_val = casted_value->static_value;
- return ir_analyze_void(ira, &store_ptr_instruction->base);
- }
- }
-
- if (ptr->static_value.ok) {
- // This memory location is transforming from known at compile time to known at runtime.
- // We must emit our own var ptr instruction.
- ptr->static_value.ok = false;
- IrInstruction *new_ptr_inst;
- if (ptr->id == IrInstructionIdVarPtr) {
- IrInstructionVarPtr *var_ptr_inst = (IrInstructionVarPtr *)ptr;
- VariableTableEntry *var = var_ptr_inst->var;
- new_ptr_inst = ir_build_var_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, var);
- assert(var->mem_slot_index != SIZE_MAX);
- ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
- mem_slot->ok = false;
- } else if (ptr->id == IrInstructionIdFieldPtr) {
- zig_panic("TODO");
- } else if (ptr->id == IrInstructionIdElemPtr) {
- zig_panic("TODO");
- } else {
- zig_unreachable();
- }
- new_ptr_inst->type_entry = ptr->type_entry;
- ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, new_ptr_inst, casted_value);
- return ir_analyze_void(ira, &store_ptr_instruction->base);
- }
- ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value);
- return ira->codegen->builtin_types.entry_void;
-}
-static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) {
- IrInstruction *expr_value = typeof_instruction->value->other;
- TypeTableEntry *type_entry = expr_value->type_entry;
- switch (type_entry->id) {
- case TypeTableEntryIdInvalid:
- return type_entry;
- case TypeTableEntryIdVar:
- add_node_error(ira->codegen, expr_value->source_node,
- buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name)));
- return ira->codegen->builtin_types.entry_invalid;
- case TypeTableEntryIdNumLitFloat:
- case TypeTableEntryIdNumLitInt:
- case TypeTableEntryIdUndefLit:
- case TypeTableEntryIdNullLit:
- case TypeTableEntryIdNamespace:
- case TypeTableEntryIdBlock:
- case TypeTableEntryIdGenericFn:
- case TypeTableEntryIdMetaType:
- case TypeTableEntryIdVoid:
- case TypeTableEntryIdBool:
- case TypeTableEntryIdUnreachable:
- case TypeTableEntryIdInt:
- case TypeTableEntryIdFloat:
- case TypeTableEntryIdPointer:
- case TypeTableEntryIdArray:
- case TypeTableEntryIdStruct:
- case TypeTableEntryIdMaybe:
- case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
- case TypeTableEntryIdEnum:
- case TypeTableEntryIdUnion:
- case TypeTableEntryIdFn:
- case TypeTableEntryIdTypeDecl:
- {
- ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false);
- // TODO depends_on_compile_var should be set based on whether the type of the expression
- // depends_on_compile_var. but we currently don't have a thing to tell us if the type of
- // something depends on a compile var
- out_val->data.x_type = type_entry;
-
- return ira->codegen->builtin_types.entry_type;
- }
- }
-
- zig_unreachable();
-}
-
-static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
- IrInstructionToPtrType *to_ptr_type_instruction)
-{
- IrInstruction *type_value = to_ptr_type_instruction->value->other;
- TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
- if (type_entry->id == TypeTableEntryIdInvalid)
- return type_entry;
-
- TypeTableEntry *ptr_type;
- if (type_entry->id == TypeTableEntryIdArray) {
- ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false);
- } else if (is_slice(type_entry)) {
- ptr_type = type_entry->data.structure.fields[0].type_entry;
- } else {
- add_node_error(ira->codegen, to_ptr_type_instruction->base.source_node,
- buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
-
- ConstExprValue *out_val = ir_build_const_from(ira, &to_ptr_type_instruction->base,
- type_value->static_value.depends_on_compile_var);
- out_val->data.x_type = ptr_type;
- return ira->codegen->builtin_types.entry_type;
-}
-
-static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
- IrInstructionPtrTypeChild *ptr_type_child_instruction)
-{
- IrInstruction *type_value = ptr_type_child_instruction->value->other;
- TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
- if (type_entry->id == TypeTableEntryIdInvalid)
- return type_entry;
-
- if (type_entry->id != TypeTableEntryIdPointer) {
- add_node_error(ira->codegen, ptr_type_child_instruction->base.source_node,
- buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
-
- ConstExprValue *out_val = ir_build_const_from(ira, &ptr_type_child_instruction->base,
- type_value->static_value.depends_on_compile_var);
- out_val->data.x_type = type_entry->data.pointer.child_type;
- return ira->codegen->builtin_types.entry_type;
-}
-
-static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
- switch (instruction->id) {
- case IrInstructionIdInvalid:
- zig_unreachable();
- case IrInstructionIdReturn:
- return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
- case IrInstructionIdConst:
- return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction);
- case IrInstructionIdUnOp:
- return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction);
- case IrInstructionIdBinOp:
- return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
- case IrInstructionIdDeclVar:
- return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction);
- case IrInstructionIdLoadPtr:
- return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction);
- case IrInstructionIdStorePtr:
- return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction);
- case IrInstructionIdElemPtr:
- return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction);
- case IrInstructionIdVarPtr:
- return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
- case IrInstructionIdFieldPtr:
- return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction);
- case IrInstructionIdReadField:
- return ir_analyze_instruction_read_field(ira, (IrInstructionReadField *)instruction);
- case IrInstructionIdCall:
- return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
- case IrInstructionIdBr:
- return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction);
- case IrInstructionIdCondBr:
- return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction);
- case IrInstructionIdBuiltinCall:
- return ir_analyze_instruction_builtin_call(ira, (IrInstructionBuiltinCall *)instruction);
- case IrInstructionIdUnreachable:
- return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction);
- case IrInstructionIdPhi:
- return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction);
- case IrInstructionIdTypeOf:
- return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction);
- case IrInstructionIdToPtrType:
- return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);
- case IrInstructionIdPtrTypeChild:
- return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
- case IrInstructionIdSwitchBr:
- case IrInstructionIdCast:
- case IrInstructionIdContainerInitList:
- case IrInstructionIdContainerInitFields:
- case IrInstructionIdStructFieldPtr:
- zig_panic("TODO analyze more instructions");
- }
- zig_unreachable();
-}
-
-static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {
- TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);
- instruction->type_entry = instruction_type;
- if (instruction->other) {
- instruction->other->type_entry = instruction_type;
- } else {
- assert(instruction_type->id == TypeTableEntryIdInvalid ||
- instruction_type->id == TypeTableEntryIdUnreachable);
- instruction->other = instruction;
- }
- return instruction_type;
-}
-
-// This function attempts to evaluate IR code while doing type checking and other analysis.
-// It emits a new IrExecutable which is partially evaluated IR code.
-TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec,
- TypeTableEntry *expected_type, AstNode *expected_type_source_node)
-{
- IrAnalyze ir_analyze_data = {};
- IrAnalyze *ira = &ir_analyze_data;
- ira->codegen = codegen;
- ira->explicit_return_type = expected_type;
-
- ira->old_irb.codegen = codegen;
- ira->old_irb.exec = old_exec;
-
- ira->new_irb.codegen = codegen;
- ira->new_irb.exec = new_exec;
-
- ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count;
- ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count);
-
- IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);
- IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb);
- ir_ref_bb(new_entry_bb);
- ira->old_irb.current_basic_block = old_entry_bb;
- ira->new_irb.current_basic_block = new_entry_bb;
- ira->block_queue_index = 0;
- ira->instruction_index = 0;
-
- while (ira->block_queue_index < ira->old_bb_queue.length) {
- IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index);
-
- if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) {
- ira->instruction_index += 1;
- continue;
- }
-
- TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction);
-
- // unreachable instructions do their own control flow.
- if (return_type->id == TypeTableEntryIdUnreachable)
- continue;
-
- ira->instruction_index += 1;
- }
-
- return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items,
- ira->implicit_return_type_list.length);
-}
+//static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeFieldAccessExpr);
+//
+// AstNode **struct_expr_node = &node->data.field_access_expr.struct_expr;
+// TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node);
+// Buf *field_name = node->data.field_access_expr.field_name;
+//
+// if (struct_type->id == TypeTableEntryIdInvalid) {
+// return struct_type;
+// } else if (is_container_ref(struct_type)) {
+// return analyze_container_member_access(g, field_name, node, struct_type);
+// } else if (struct_type->id == TypeTableEntryIdArray) {
+// if (buf_eql_str(field_name, "len")) {
+// return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
+// struct_type->data.array.len, false);
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
+// buf_ptr(&struct_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// } else if (struct_type->id == TypeTableEntryIdMetaType) {
+// TypeTableEntry *child_type = resolve_type(g, *struct_expr_node);
+//
+// if (child_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (child_type->id == TypeTableEntryIdEnum) {
+// AstNode *container_init_node = node->data.field_access_expr.container_init_expr_node;
+// AstNode *value_node;
+// if (container_init_node) {
+// assert(container_init_node->type == NodeTypeContainerInitExpr);
+// size_t param_count = container_init_node->data.container_init_expr.entries.length;
+// if (param_count > 1) {
+// AstNode *first_invalid_node = container_init_node->data.container_init_expr.entries.at(1);
+// add_node_error(g, first_executing_node(first_invalid_node),
+// buf_sprintf("enum values accept only one parameter"));
+// return child_type;
+// } else {
+// if (param_count == 1) {
+// value_node = container_init_node->data.container_init_expr.entries.at(0);
+// } else {
+// value_node = nullptr;
+// }
+// container_init_node->data.container_init_expr.enum_type = child_type;
+// }
+// } else {
+// value_node = nullptr;
+// }
+// return analyze_enum_value_expr(g, import, context, node, value_node, child_type, field_name, node);
+// } else if (child_type->id == TypeTableEntryIdStruct) {
+// BlockContext *container_block_context = get_container_block_context(child_type);
+// auto entry = container_block_context->decl_table.maybe_get(field_name);
+// AstNode *decl_node = entry ? entry->value : nullptr;
+// if (decl_node) {
+// bool pointer_only = false;
+// return analyze_decl_ref(g, node, decl_node, pointer_only, context, false);
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("container '%s' has no member called '%s'",
+// buf_ptr(&child_type->name), buf_ptr(field_name)));
+// return g->builtin_types.entry_invalid;
+// }
+// } else if (child_type->id == TypeTableEntryIdPureError) {
+// return analyze_error_literal_expr(g, import, context, node, field_name);
+// } else if (child_type->id == TypeTableEntryIdInt) {
+// bool depends_on_compile_var =
+// get_resolved_expr(*struct_expr_node)->const_val.depends_on_compile_var;
+// if (buf_eql_str(field_name, "bit_count")) {
+// return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
+// child_type->data.integral.bit_count, depends_on_compile_var);
+// } else if (buf_eql_str(field_name, "is_signed")) {
+// return resolve_expr_const_val_as_bool(g, node, child_type->data.integral.is_signed,
+// depends_on_compile_var);
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("type '%s' has no member called '%s'",
+// buf_ptr(&child_type->name), buf_ptr(field_name)));
+// return g->builtin_types.entry_invalid;
+// }
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// } else if (struct_type->id == TypeTableEntryIdNamespace) {
+// ConstExprValue *const_val = &get_resolved_expr(*struct_expr_node)->const_val;
+// assert(const_val->ok);
+// ImportTableEntry *namespace_import = const_val->data.x_import;
+// AstNode *decl_node = find_decl(namespace_import->block_context, field_name);
+// if (!decl_node) {
+// // we must now resolve all the use decls
+// for (size_t i = 0; i < namespace_import->use_decls.length; i += 1) {
+// AstNode *use_decl_node = namespace_import->use_decls.at(i);
+// if (!get_resolved_expr(use_decl_node->data.use.expr)->type_entry) {
+// preview_use_decl(g, use_decl_node);
+// }
+// resolve_use_decl(g, use_decl_node);
+// }
+// decl_node = find_decl(namespace_import->block_context, field_name);
+// }
+// if (decl_node) {
+// TopLevelDecl *tld = get_as_top_level_decl(decl_node);
+// if (tld->visib_mod == VisibModPrivate && decl_node->owner != import) {
+// ErrorMsg *msg = add_node_error(g, node,
+// buf_sprintf("'%s' is private", buf_ptr(field_name)));
+// add_error_note(g, msg, decl_node, buf_sprintf("declared here"));
+// }
+// bool pointer_only = false;
+// return analyze_decl_ref(g, node, decl_node, pointer_only, context,
+// const_val->depends_on_compile_var);
+// } else {
+// const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)";
+// add_node_error(g, node,
+// buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name));
+// return g->builtin_types.entry_invalid;
+// }
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//}
+//
-static bool ir_builtin_call_has_side_effects(IrInstructionBuiltinCall *call_instruction) {
- switch (call_instruction->fn->id) {
- case BuiltinFnIdInvalid:
- zig_unreachable();
- case BuiltinFnIdMemcpy:
- case BuiltinFnIdMemset:
- case BuiltinFnIdAddWithOverflow:
- case BuiltinFnIdSubWithOverflow:
- case BuiltinFnIdMulWithOverflow:
- case BuiltinFnIdShlWithOverflow:
- case BuiltinFnIdCInclude:
- case BuiltinFnIdCDefine:
- case BuiltinFnIdCUndef:
- case BuiltinFnIdImport:
- case BuiltinFnIdCImport:
- case BuiltinFnIdBreakpoint:
- case BuiltinFnIdEmbedFile:
- case BuiltinFnIdCmpExchange:
- case BuiltinFnIdFence:
- case BuiltinFnIdUnreachable:
- case BuiltinFnIdSetFnTest:
- case BuiltinFnIdSetFnVisible:
- case BuiltinFnIdSetFnStaticEval:
- case BuiltinFnIdSetFnNoInline:
- case BuiltinFnIdSetDebugSafety:
- return true;
- case BuiltinFnIdSizeof:
- case BuiltinFnIdAlignof:
- case BuiltinFnIdMaxValue:
- case BuiltinFnIdMinValue:
- case BuiltinFnIdMemberCount:
- case BuiltinFnIdTypeof:
- case BuiltinFnIdCompileVar:
- case BuiltinFnIdCompileErr:
- case BuiltinFnIdConstEval:
- case BuiltinFnIdCtz:
- case BuiltinFnIdClz:
- case BuiltinFnIdErrName:
- case BuiltinFnIdReturnAddress:
- case BuiltinFnIdFrameAddress:
- case BuiltinFnIdDivExact:
- case BuiltinFnIdTruncate:
- case BuiltinFnIdIntType:
- return false;
- }
- zig_unreachable();
-}
+//static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
+// AstNode *lhs_node, LValPurpose purpose, bool is_ptr_const)
+//{
+// TypeTableEntry *expected_rhs_type = nullptr;
+// lhs_node->block_context = block_context;
+// if (lhs_node->type == NodeTypeSymbol) {
+// bool pointer_only = purpose == LValPurposeAddressOf;
+// expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node, pointer_only);
+// if (expected_rhs_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+// if (purpose != LValPurposeAddressOf) {
+// Buf *name = lhs_node->data.symbol_expr.symbol;
+// VariableTableEntry *var = find_variable(g, block_context, name);
+// if (var) {
+// if (var->src_is_const) {
+// add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant"));
+// expected_rhs_type = g->builtin_types.entry_invalid;
+// } else {
+// expected_rhs_type = var->type;
+// get_resolved_expr(lhs_node)->variable = var;
+// }
+// } else {
+// add_node_error(g, lhs_node,
+// buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
+// expected_rhs_type = g->builtin_types.entry_invalid;
+// }
+// }
+// } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
+// expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node, purpose);
+// } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
+// expected_rhs_type = analyze_field_access_expr(g, import, block_context, nullptr, lhs_node);
+// } else if (lhs_node->type == NodeTypePrefixOpExpr &&
+// lhs_node->data.prefix_op_expr.prefix_op == PrefixOpDereference)
+// {
+// assert(purpose == LValPurposeAssign);
+// AstNode *target_node = lhs_node->data.prefix_op_expr.primary_expr;
+// TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, target_node);
+// if (type_entry->id == TypeTableEntryIdInvalid) {
+// expected_rhs_type = type_entry;
+// } else if (type_entry->id == TypeTableEntryIdPointer) {
+// expected_rhs_type = type_entry->data.pointer.child_type;
+// } else {
+// add_node_error(g, target_node,
+// buf_sprintf("indirection requires pointer operand ('%s' invalid)",
+// buf_ptr(&type_entry->name)));
+// expected_rhs_type = g->builtin_types.entry_invalid;
+// }
+// } else {
+// if (purpose == LValPurposeAssign) {
+// add_node_error(g, lhs_node, buf_sprintf("invalid assignment target"));
+// expected_rhs_type = g->builtin_types.entry_invalid;
+// } else if (purpose == LValPurposeAddressOf) {
+// TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, lhs_node);
+// if (type_entry->id == TypeTableEntryIdInvalid) {
+// expected_rhs_type = g->builtin_types.entry_invalid;
+// } else if (type_entry->id == TypeTableEntryIdMetaType) {
+// expected_rhs_type = type_entry;
+// } else {
+// add_node_error(g, lhs_node, buf_sprintf("invalid addressof target"));
+// expected_rhs_type = g->builtin_types.entry_invalid;
+// }
+// }
+// }
+// assert(expected_rhs_type);
+// return expected_rhs_type;
+//}
-bool ir_has_side_effects(IrInstruction *instruction) {
- switch (instruction->id) {
- case IrInstructionIdInvalid:
- zig_unreachable();
- case IrInstructionIdBr:
- case IrInstructionIdCondBr:
- case IrInstructionIdSwitchBr:
- case IrInstructionIdDeclVar:
- case IrInstructionIdStorePtr:
- case IrInstructionIdCall:
- case IrInstructionIdReturn:
- case IrInstructionIdUnreachable:
- return true;
- case IrInstructionIdPhi:
- case IrInstructionIdUnOp:
- case IrInstructionIdBinOp:
- case IrInstructionIdLoadPtr:
- case IrInstructionIdConst:
- case IrInstructionIdCast:
- case IrInstructionIdContainerInitList:
- case IrInstructionIdContainerInitFields:
- case IrInstructionIdFieldPtr:
- case IrInstructionIdElemPtr:
- case IrInstructionIdVarPtr:
- case IrInstructionIdTypeOf:
- case IrInstructionIdToPtrType:
- case IrInstructionIdPtrTypeChild:
- case IrInstructionIdReadField:
- case IrInstructionIdStructFieldPtr:
- return false;
- case IrInstructionIdBuiltinCall:
- return ir_builtin_call_has_side_effects((IrInstructionBuiltinCall *)instruction);
- }
- zig_unreachable();
-}
-IrInstruction *ir_exec_const_result(IrExecutable *exec) {
- if (exec->basic_block_list.length != 1)
- return nullptr;
- IrBasicBlock *bb = exec->basic_block_list.at(0);
- if (bb->instruction_list.length != 1)
- return nullptr;
+//static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeBinOpExpr);
+// BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
+// switch (bin_op_type) {
+// case BinOpTypeAssign:
+// case BinOpTypeAssignTimes:
+// case BinOpTypeAssignTimesWrap:
+// case BinOpTypeAssignDiv:
+// case BinOpTypeAssignMod:
+// case BinOpTypeAssignPlus:
+// case BinOpTypeAssignPlusWrap:
+// case BinOpTypeAssignMinus:
+// case BinOpTypeAssignMinusWrap:
+// case BinOpTypeAssignBitShiftLeft:
+// case BinOpTypeAssignBitShiftLeftWrap:
+// case BinOpTypeAssignBitShiftRight:
+// case BinOpTypeAssignBitAnd:
+// case BinOpTypeAssignBitXor:
+// case BinOpTypeAssignBitOr:
+// case BinOpTypeAssignBoolAnd:
+// case BinOpTypeAssignBoolOr:
+// {
+// AstNode *lhs_node = node->data.bin_op_expr.op1;
+//
+// TypeTableEntry *expected_rhs_type = analyze_lvalue(g, import, context, lhs_node,
+// LValPurposeAssign, false);
+// if (expected_rhs_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (!is_op_allowed(expected_rhs_type, node->data.bin_op_expr.bin_op)) {
+// if (expected_rhs_type->id != TypeTableEntryIdInvalid) {
+// add_node_error(g, lhs_node,
+// buf_sprintf("operator not allowed for type '%s'",
+// buf_ptr(&expected_rhs_type->name)));
+// }
+// }
+//
+// analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
+// // not const ok because expression has side effects
+// return g->builtin_types.entry_void;
+// }
+// case BinOpTypeBoolOr:
+// case BinOpTypeBoolAnd:
+// return analyze_logic_bin_op_expr(g, import, context, node);
+// case BinOpTypeCmpEq:
+// case BinOpTypeCmpNotEq:
+// case BinOpTypeCmpLessThan:
+// case BinOpTypeCmpGreaterThan:
+// case BinOpTypeCmpLessOrEq:
+// case BinOpTypeCmpGreaterOrEq:
+// return analyze_bool_bin_op_expr(g, import, context, node);
+// case BinOpTypeBinOr:
+// case BinOpTypeBinXor:
+// case BinOpTypeBinAnd:
+// case BinOpTypeBitShiftLeft:
+// case BinOpTypeBitShiftLeftWrap:
+// case BinOpTypeBitShiftRight:
+// case BinOpTypeAdd:
+// case BinOpTypeAddWrap:
+// case BinOpTypeSub:
+// case BinOpTypeSubWrap:
+// case BinOpTypeMult:
+// case BinOpTypeMultWrap:
+// case BinOpTypeDiv:
+// case BinOpTypeMod:
+// {
+// AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
+// AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
+// TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, *op1);
+// TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, *op2);
+//
+// AstNode *op_nodes[] = {*op1, *op2};
+// TypeTableEntry *op_types[] = {lhs_type, rhs_type};
+//
+// TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
+// op_nodes, op_types, 2);
+//
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// }
+//
+// if (resolved_type->id == TypeTableEntryIdInt ||
+// resolved_type->id == TypeTableEntryIdNumLitInt)
+// {
+// // int
+// } else if ((resolved_type->id == TypeTableEntryIdFloat ||
+// resolved_type->id == TypeTableEntryIdNumLitFloat) &&
+// (bin_op_type == BinOpTypeAdd ||
+// bin_op_type == BinOpTypeSub ||
+// bin_op_type == BinOpTypeMult ||
+// bin_op_type == BinOpTypeDiv ||
+// bin_op_type == BinOpTypeMod))
+// {
+// // float
+// } else {
+// add_node_error(g, node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
+// buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
+// ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
+// if (!op1_val->ok || !op2_val->ok) {
+// return resolved_type;
+// }
+//
+// ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
+// int err;
+// if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type,
+// op2_val, resolved_type, out_val)))
+// {
+// if (err == ErrorDivByZero) {
+// add_node_error(g, node, buf_sprintf("division by zero is undefined"));
+// return g->builtin_types.entry_invalid;
+// } else if (err == ErrorOverflow) {
+// add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type"));
+// return g->builtin_types.entry_invalid;
+// }
+// return g->builtin_types.entry_invalid;
+// }
+//
+// num_lit_fits_in_other_type(g, node, resolved_type);
+// return resolved_type;
+// }
+// case BinOpTypeUnwrapMaybe:
+// {
+// AstNode *op1 = node->data.bin_op_expr.op1;
+// AstNode *op2 = node->data.bin_op_expr.op2;
+// TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
+//
+// if (lhs_type->id == TypeTableEntryIdInvalid) {
+// return lhs_type;
+// } else if (lhs_type->id == TypeTableEntryIdMaybe) {
+// TypeTableEntry *child_type = lhs_type->data.maybe.child_type;
+// analyze_expression(g, import, context, child_type, op2);
+// return child_type;
+// } else {
+// add_node_error(g, op1,
+// buf_sprintf("expected maybe type, got '%s'",
+// buf_ptr(&lhs_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// case BinOpTypeArrayCat:
+// {
+// AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
+// AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
+//
+// TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
+// TypeTableEntry *child_type;
+// if (op1_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (op1_type->id == TypeTableEntryIdArray) {
+// child_type = op1_type->data.array.child_type;
+// } else if (op1_type->id == TypeTableEntryIdPointer &&
+// op1_type->data.pointer.child_type == g->builtin_types.entry_u8) {
+// child_type = op1_type->data.pointer.child_type;
+// } else {
+// add_node_error(g, *op1, buf_sprintf("expected array or C string literal, got '%s'",
+// buf_ptr(&op1_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
+//
+// if (op2_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (op2_type->id == TypeTableEntryIdArray) {
+// if (op2_type->data.array.child_type != child_type) {
+// add_node_error(g, *op2, buf_sprintf("expected array of type '%s', got '%s'",
+// buf_ptr(&child_type->name),
+// buf_ptr(&op2_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// } else if (op2_type->id == TypeTableEntryIdPointer &&
+// op2_type->data.pointer.child_type == g->builtin_types.entry_u8) {
+// } else {
+// add_node_error(g, *op2, buf_sprintf("expected array or C string literal, got '%s'",
+// buf_ptr(&op2_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
+// ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
+//
+// AstNode *bad_node;
+// if (!op1_val->ok) {
+// bad_node = *op1;
+// } else if (!op2_val->ok) {
+// bad_node = *op2;
+// } else {
+// bad_node = nullptr;
+// }
+// if (bad_node) {
+// add_node_error(g, bad_node, buf_sprintf("array concatenation requires constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+// const_val->ok = true;
+// const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
+// op2_val->depends_on_compile_var;
+//
+// if (op1_type->id == TypeTableEntryIdArray) {
+// uint64_t new_len = op1_type->data.array.len + op2_type->data.array.len;
+// const_val->data.x_array.fields = allocate<ConstExprValue*>(new_len);
+// uint64_t next_index = 0;
+// for (uint64_t i = 0; i < op1_type->data.array.len; i += 1, next_index += 1) {
+// const_val->data.x_array.fields[next_index] = op1_val->data.x_array.fields[i];
+// }
+// for (uint64_t i = 0; i < op2_type->data.array.len; i += 1, next_index += 1) {
+// const_val->data.x_array.fields[next_index] = op2_val->data.x_array.fields[i];
+// }
+// return get_array_type(g, child_type, new_len);
+// } else if (op1_type->id == TypeTableEntryIdPointer) {
+// if (!op1_val->data.x_ptr.is_c_str) {
+// add_node_error(g, *op1,
+// buf_sprintf("expected array or C string literal, got '%s'",
+// buf_ptr(&op1_type->name)));
+// return g->builtin_types.entry_invalid;
+// } else if (!op2_val->data.x_ptr.is_c_str) {
+// add_node_error(g, *op2,
+// buf_sprintf("expected array or C string literal, got '%s'",
+// buf_ptr(&op2_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// const_val->data.x_ptr.is_c_str = true;
+// const_val->data.x_ptr.len = op1_val->data.x_ptr.len + op2_val->data.x_ptr.len - 1;
+// const_val->data.x_ptr.ptr = allocate<ConstExprValue*>(const_val->data.x_ptr.len);
+// uint64_t next_index = 0;
+// for (uint64_t i = 0; i < op1_val->data.x_ptr.len - 1; i += 1, next_index += 1) {
+// const_val->data.x_ptr.ptr[next_index] = op1_val->data.x_ptr.ptr[i];
+// }
+// for (uint64_t i = 0; i < op2_val->data.x_ptr.len; i += 1, next_index += 1) {
+// const_val->data.x_ptr.ptr[next_index] = op2_val->data.x_ptr.ptr[i];
+// }
+// return op1_type;
+// } else {
+// zig_unreachable();
+// }
+// }
+// case BinOpTypeArrayMult:
+// return analyze_array_mult(g, import, context, expected_type, node);
+// case BinOpTypeInvalid:
+// zig_unreachable();
+// }
+// zig_unreachable();
+//}
- IrInstruction *only_inst = bb->instruction_list.at(0);
- if (only_inst->id != IrInstructionIdReturn)
- return nullptr;
- IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst;
- IrInstruction *value = ret_inst->value;
- assert(value->static_value.ok);
- return value;
-}
+//static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// AstNode *node)
+//{
+// assert(node->type == NodeTypeBinOpExpr);
+// BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
+//
+// AstNode **op1 = &node->data.bin_op_expr.op1;
+// AstNode **op2 = &node->data.bin_op_expr.op2;
+// TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
+// TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
+//
+// AstNode *op_nodes[] = {*op1, *op2};
+// TypeTableEntry *op_types[] = {op1_type, op2_type};
+//
+// TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
+// op_nodes, op_types, 2);
+//
+// bool is_equality_cmp = (bin_op_type == BinOpTypeCmpEq || bin_op_type == BinOpTypeCmpNotEq);
+//
+// switch (resolved_type->id) {
+// case TypeTableEntryIdInvalid:
+// return g->builtin_types.entry_invalid;
+//
+// case TypeTableEntryIdNumLitFloat:
+// case TypeTableEntryIdNumLitInt:
+// case TypeTableEntryIdInt:
+// case TypeTableEntryIdFloat:
+// break;
+//
+// case TypeTableEntryIdBool:
+// case TypeTableEntryIdMetaType:
+// case TypeTableEntryIdVoid:
+// case TypeTableEntryIdPointer:
+// case TypeTableEntryIdPureError:
+// case TypeTableEntryIdFn:
+// case TypeTableEntryIdTypeDecl:
+// case TypeTableEntryIdNamespace:
+// case TypeTableEntryIdBlock:
+// case TypeTableEntryIdGenericFn:
+// if (!is_equality_cmp) {
+// add_node_error(g, node,
+// buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// break;
+//
+// case TypeTableEntryIdEnum:
+// if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) {
+// add_node_error(g, node,
+// buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// break;
+//
+// case TypeTableEntryIdUnreachable:
+// case TypeTableEntryIdArray:
+// case TypeTableEntryIdStruct:
+// case TypeTableEntryIdUndefLit:
+// case TypeTableEntryIdNullLit:
+// case TypeTableEntryIdMaybe:
+// case TypeTableEntryIdErrorUnion:
+// case TypeTableEntryIdUnion:
+// add_node_error(g, node,
+// buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
+// return g->builtin_types.entry_invalid;
+//
+// case TypeTableEntryIdVar:
+// zig_unreachable();
+// }
+//
+// ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
+// ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
+// if (!op1_val->ok || !op2_val->ok) {
+// return g->builtin_types.entry_bool;
+// }
+//
+//
+// ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
+// eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val);
+// return g->builtin_types.entry_bool;
+//
+//}
+//
+////
+//static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
+// TypeTableEntry *expected_type, AstNode *node,
+// AstNode **then_node, AstNode **else_node, bool cond_is_const, bool cond_bool_val)
+//{
+// if (!*else_node) {
+// *else_node = create_ast_void_node(g, import, node);
+// normalize_parent_ptrs(node);
+// }
+//
+// BlockContext *then_context;
+// BlockContext *else_context;
+// if (cond_is_const) {
+// if (cond_bool_val) {
+// then_context = parent_context;
+// else_context = new_block_context(node, parent_context);
+//
+// else_context->codegen_excluded = true;
+// } else {
+// then_context = new_block_context(node, parent_context);
+// else_context = parent_context;
+//
+// then_context->codegen_excluded = true;
+// }
+// } else {
+// then_context = parent_context;
+// else_context = parent_context;
+// }
+//
+// TypeTableEntry *then_type = nullptr;
+// TypeTableEntry *else_type = nullptr;
+//
+// if (!then_context->codegen_excluded) {
+// then_type = analyze_expression(g, import, then_context, expected_type, *then_node);
+// if (then_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// if (!else_context->codegen_excluded) {
+// else_type = analyze_expression(g, import, else_context, expected_type, *else_node);
+// if (else_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+// }
+//
+// TypeTableEntry *result_type;
+// if (then_context->codegen_excluded) {
+// result_type = else_type;
+// } else if (else_context->codegen_excluded) {
+// result_type = then_type;
+// } else if (expected_type) {
+// result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
+// } else {
+// AstNode *op_nodes[] = {*then_node, *else_node};
+// TypeTableEntry *op_types[] = {then_type, else_type};
+// result_type = resolve_peer_type_compatibility(g, import, parent_context, node, op_nodes, op_types, 2);
+// }
+//
+// if (!cond_is_const) {
+// return add_error_if_type_is_num_lit(g, result_type, node);
+// }
+//
+// ConstExprValue *other_const_val;
+// if (cond_bool_val) {
+// other_const_val = &get_resolved_expr(*then_node)->const_val;
+// } else {
+// other_const_val = &get_resolved_expr(*else_node)->const_val;
+// }
+// if (!other_const_val->ok) {
+// return add_error_if_type_is_num_lit(g, result_type, node);
+// }
+//
+// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+// *const_val = *other_const_val;
+// // the condition depends on a compile var, so the entire if statement does too
+// const_val->depends_on_compile_var = true;
+// return result_type;
+//}
+//
+//static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeIfVarExpr);
+//
+// BlockContext *child_context = new_block_context(node, parent_context);
+//
+// analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true,
+// nullptr, node->data.if_var_expr.var_is_ptr);
+// VariableTableEntry *var = node->data.if_var_expr.var_decl.variable;
+// if (var->type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+// AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr;
+// ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val;
+// bool cond_is_const = var_const_val->ok;
+// bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false;
+//
+//
+// AstNode **then_node = &node->data.if_var_expr.then_block;
+// AstNode **else_node = &node->data.if_var_expr.else_node;
+//
+// return analyze_if(g, import, child_context, expected_type,
+// node, then_node, else_node, cond_is_const, cond_bool_val);
+//}
+//
+//static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type,
+// TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry)
+//{
+// ErrorMsg *msg = add_node_error(g, node,
+// buf_sprintf("function called as method of '%s', but first parameter is of type '%s'",
+// buf_ptr(&container_type->name),
+// buf_ptr(&expected_param_type->name)));
+// if (fn_table_entry) {
+// add_error_note(g, msg, fn_table_entry->proto_node, buf_sprintf("function declared here"));
+// }
+// return g->builtin_types.entry_invalid;
+//}
+//
+//// Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known
+//// at compile time. Otherwise this is a function pointer call.
+//static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type,
+// AstNode *struct_node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// if (fn_type->id == TypeTableEntryIdInvalid) {
+// return fn_type;
+// }
+//
+// // The function call might include inline parameters which we need to ignore according to the
+// // fn_type.
+// FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
+// AstNode *generic_proto_node = fn_table_entry ?
+// fn_table_entry->proto_node->data.fn_proto.generic_proto_node : nullptr;
+//
+// // count parameters
+// size_t struct_node_1_or_0 = struct_node ? 1 : 0;
+// size_t src_param_count = fn_type->data.fn.fn_type_id.param_count +
+// (generic_proto_node ? generic_proto_node->data.fn_proto.inline_arg_count : 0);
+// size_t call_param_count = node->data.fn_call_expr.params.length;
+// size_t expect_arg_count = src_param_count - struct_node_1_or_0;
+//
+// bool ok_invocation = true;
+//
+// if (fn_type->data.fn.fn_type_id.is_var_args) {
+// if (call_param_count < expect_arg_count) {
+// ok_invocation = false;
+// add_node_error(g, node,
+// buf_sprintf("expected at least %zu arguments, got %zu", src_param_count, call_param_count));
+// }
+// } else if (expect_arg_count != call_param_count) {
+// ok_invocation = false;
+// add_node_error(g, node,
+// buf_sprintf("expected %zu arguments, got %zu", expect_arg_count, call_param_count));
+// }
+//
+// bool all_args_const_expr = true;
+//
+// if (struct_node) {
+// Expr *struct_expr = get_resolved_expr(struct_node);
+// ConstExprValue *struct_const_val = &struct_expr->const_val;
+// if (!struct_const_val->ok) {
+// all_args_const_expr = false;
+// }
+//
+// FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[0];
+// TypeTableEntry *expected_param_type = param_info->type;
+// TypeTableEntry *container_bare_type = container_ref_type(struct_expr->type_entry);
+// if (is_container_ref(expected_param_type)) {
+// TypeTableEntry *param_bare_type = container_ref_type(expected_param_type);
+// if (param_bare_type != container_bare_type) {
+// return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
+// }
+// } else {
+// return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
+// }
+// }
+//
+// // analyze each parameter. in the case of a method, we already analyzed the
+// // first parameter in order to figure out which struct we were calling a method on.
+// size_t next_type_i = struct_node_1_or_0;
+// for (size_t call_i = 0; call_i < call_param_count; call_i += 1) {
+// size_t proto_i = call_i + struct_node_1_or_0;
+// AstNode **param_node = &node->data.fn_call_expr.params.at(call_i);
+// // determine the expected type for each parameter
+// TypeTableEntry *expected_param_type = nullptr;
+// if (proto_i < src_param_count) {
+// if (generic_proto_node &&
+// generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
+// {
+// continue;
+// }
+//
+// FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[next_type_i];
+// next_type_i += 1;
+//
+// expected_param_type = param_info->type;
+// }
+// TypeTableEntry *param_type = analyze_expression(g, import, context, expected_param_type, *param_node);
+// if (param_type->id == TypeTableEntryIdInvalid) {
+// return param_type;
+// }
+//
+// ConstExprValue *const_arg_val = &get_resolved_expr(*param_node)->const_val;
+// if (!const_arg_val->ok) {
+// all_args_const_expr = false;
+// }
+// }
+//
+// TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
+//
+// if (return_type->id == TypeTableEntryIdInvalid) {
+// return return_type;
+// }
+//
+// ConstExprValue *result_val = &get_resolved_expr(node)->const_val;
+// if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) {
+// if (fn_table_entry->anal_state == FnAnalStateReady) {
+// analyze_fn_body(g, fn_table_entry);
+// if (fn_table_entry->proto_node->data.fn_proto.skip) {
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// if (all_args_const_expr) {
+// if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) {
+// if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) {
+// // function evaluation generated an error
+// return g->builtin_types.entry_invalid;
+// }
+// return return_type;
+// }
+// }
+// }
+// if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure || fn_table_entry->want_pure == WantPureFalse) {
+// // calling an impure fn is impure
+// mark_impure_fn(g, context, node);
+// if (fn_table_entry && fn_table_entry->want_pure == WantPureTrue) {
+// return g->builtin_types.entry_invalid;
+// }
+// }
+//
+// // TODO
+// //if (handle_is_ptr(return_type)) {
+// // if (context->fn_entry) {
+// // context->fn_entry->cast_alloca_list.append(node);
+// // } else if (!result_val->ok) {
+// // add_node_error(g, node, buf_sprintf("unable to evaluate constant expression"));
+// // }
+// //}
+//
+// return return_type;
+//}
+//
+//static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableEntry *import,
+// BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *call_node,
+// FnTableEntry *fn_table_entry, AstNode *struct_node)
+//{
+// assert(call_node->type == NodeTypeFnCallExpr);
+// assert(fn_table_entry);
+//
+// AstNode *decl_node = fn_table_entry->proto_node;
+//
+// // count parameters
+// size_t struct_node_1_or_0 = (struct_node ? 1 : 0);
+// size_t src_param_count = decl_node->data.fn_proto.params.length;
+// size_t call_param_count = call_node->data.fn_call_expr.params.length;
+//
+// if (src_param_count != call_param_count + struct_node_1_or_0) {
+// add_node_error(g, call_node,
+// buf_sprintf("expected %zu arguments, got %zu", src_param_count - struct_node_1_or_0, call_param_count));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// size_t inline_or_var_type_arg_count = decl_node->data.fn_proto.inline_or_var_type_arg_count;
+// assert(inline_or_var_type_arg_count > 0);
+//
+// BlockContext *child_context = decl_node->owner->block_context;
+// size_t next_generic_param_index = 0;
+//
+// GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1);
+// generic_fn_type_id->decl_node = decl_node;
+// generic_fn_type_id->generic_param_count = inline_or_var_type_arg_count;
+// generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_or_var_type_arg_count);
+//
+// size_t next_impl_i = 0;
+// for (size_t call_i = 0; call_i < call_param_count; call_i += 1) {
+// size_t proto_i = call_i + struct_node_1_or_0;
+// AstNode *generic_param_decl_node = decl_node->data.fn_proto.params.at(proto_i);
+// assert(generic_param_decl_node->type == NodeTypeParamDecl);
+//
+// AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type;
+// TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner, child_context,
+// *generic_param_type_node);
+// if (expected_param_type->id == TypeTableEntryIdInvalid) {
+// return expected_param_type;
+// }
+//
+// bool is_var_type = (expected_param_type->id == TypeTableEntryIdVar);
+// bool is_inline = generic_param_decl_node->data.param_decl.is_inline;
+// if (!is_inline && !is_var_type) {
+// next_impl_i += 1;
+// continue;
+// }
+//
+//
+// AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i);
+// TypeTableEntry *param_type = analyze_expression(g, import, parent_context,
+// is_var_type ? nullptr : expected_param_type, *param_node);
+// if (param_type->id == TypeTableEntryIdInvalid) {
+// return param_type;
+// }
+//
+// // set child_context so that the previous param is in scope
+// child_context = new_block_context(generic_param_decl_node, child_context);
+//
+// ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val;
+// if (is_inline && !const_val->ok) {
+// add_node_error(g, *param_node,
+// buf_sprintf("unable to evaluate constant expression for inline parameter"));
+//
+// return g->builtin_types.entry_invalid;
+// }
+//
+// VariableTableEntry *var = add_local_var_shadowable(g, generic_param_decl_node, decl_node->owner, child_context,
+// generic_param_decl_node->data.param_decl.name, param_type, true, *param_node, true);
+// // This generic function instance could be called with anything, so when this variable is read it
+// // needs to know that it depends on compile time variable data.
+// var->force_depends_on_compile_var = true;
+//
+// GenericParamValue *generic_param_value =
+// &generic_fn_type_id->generic_params[next_generic_param_index];
+// generic_param_value->type = param_type;
+// generic_param_value->node = is_inline ? *param_node : nullptr;
+// generic_param_value->impl_index = next_impl_i;
+// next_generic_param_index += 1;
+//
+// if (!is_inline) {
+// next_impl_i += 1;
+// }
+// }
+//
+// assert(next_generic_param_index == inline_or_var_type_arg_count);
+//
+// auto entry = g->generic_table.maybe_get(generic_fn_type_id);
+// FnTableEntry *impl_fn;
+// if (entry) {
+// AstNode *impl_decl_node = entry->value;
+// assert(impl_decl_node->type == NodeTypeFnProto);
+// impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
+// } else {
+// AstNode *decl_node = generic_fn_type_id->decl_node;
+// AstNode *impl_fn_def_node = ast_clone_subtree_special(decl_node->data.fn_proto.fn_def_node,
+// &g->next_node_index, AstCloneSpecialOmitInlineParams);
+// AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto;
+// impl_decl_node->data.fn_proto.inline_arg_count = 0;
+// impl_decl_node->data.fn_proto.inline_or_var_type_arg_count = 0;
+// impl_decl_node->data.fn_proto.generic_proto_node = decl_node;
+//
+// // replace var arg types with actual types
+// for (size_t generic_arg_i = 0; generic_arg_i < inline_or_var_type_arg_count; generic_arg_i += 1) {
+// GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[generic_arg_i];
+// if (!generic_param_value->node) {
+// size_t impl_i = generic_param_value->impl_index;
+// AstNode *impl_param_decl_node = impl_decl_node->data.fn_proto.params.at(impl_i);
+// assert(impl_param_decl_node->type == NodeTypeParamDecl);
+//
+// impl_param_decl_node->data.param_decl.type = create_ast_type_node(g, import,
+// generic_param_value->type, impl_param_decl_node);
+// normalize_parent_ptrs(impl_param_decl_node);
+// }
+// }
+//
+// preview_fn_proto_instance(g, import, impl_decl_node, child_context);
+// g->generic_table.put(generic_fn_type_id, impl_decl_node);
+// impl_fn = impl_decl_node->data.fn_proto.fn_table_entry;
+// }
+//
+// call_node->data.fn_call_expr.fn_entry = impl_fn;
+// return analyze_fn_call_ptr(g, import, parent_context, expected_type, call_node,
+// impl_fn->type_entry, struct_node);
+//}
+//
+//static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
+// TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *generic_fn_type)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+// assert(generic_fn_type->id == TypeTableEntryIdGenericFn);
+//
+// AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
+// assert(decl_node->type == NodeTypeContainerDecl);
+// ZigList<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params;
+//
+// size_t expected_param_count = generic_params->length;
+// size_t actual_param_count = node->data.fn_call_expr.params.length;
+//
+// if (actual_param_count != expected_param_count) {
+// add_node_error(g, first_executing_node(node),
+// buf_sprintf("expected %zu arguments, got %zu", expected_param_count, actual_param_count));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1);
+// generic_fn_type_id->decl_node = decl_node;
+// generic_fn_type_id->generic_param_count = actual_param_count;
+// generic_fn_type_id->generic_params = allocate<GenericParamValue>(actual_param_count);
+//
+// BlockContext *child_context = decl_node->owner->block_context;
+// for (size_t i = 0; i < actual_param_count; i += 1) {
+// AstNode *generic_param_decl_node = generic_params->at(i);
+// assert(generic_param_decl_node->type == NodeTypeParamDecl);
+//
+// AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type;
+//
+// TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner,
+// child_context, *generic_param_type_node);
+// if (expected_param_type->id == TypeTableEntryIdInvalid) {
+// return expected_param_type;
+// }
+//
+//
+//
+// AstNode **param_node = &node->data.fn_call_expr.params.at(i);
+//
+// TypeTableEntry *param_type = analyze_expression(g, import, parent_context, expected_param_type,
+// *param_node);
+// if (param_type->id == TypeTableEntryIdInvalid) {
+// return param_type;
+// }
+//
+// // set child_context so that the previous param is in scope
+// child_context = new_block_context(generic_param_decl_node, child_context);
+//
+// ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val;
+// if (const_val->ok) {
+// VariableTableEntry *var = add_local_var(g, generic_param_decl_node, decl_node->owner, child_context,
+// generic_param_decl_node->data.param_decl.name, param_type, true, *param_node);
+// var->force_depends_on_compile_var = true;
+// } else {
+// add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression"));
+//
+// return g->builtin_types.entry_invalid;
+// }
+//
+// GenericParamValue *generic_param_value = &generic_fn_type_id->generic_params[i];
+// generic_param_value->type = param_type;
+// generic_param_value->node = *param_node;
+// }
+//
+// auto entry = g->generic_table.maybe_get(generic_fn_type_id);
+// if (entry) {
+// AstNode *impl_decl_node = entry->value;
+// assert(impl_decl_node->type == NodeTypeContainerDecl);
+// TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
+// return resolve_expr_const_val_as_type(g, node, type_entry, false);
+// }
+//
+// // make a type from the generic parameters supplied
+// assert(decl_node->type == NodeTypeContainerDecl);
+// AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index);
+// g->generic_table.put(generic_fn_type_id, impl_decl_node);
+// scan_struct_decl(g, import, child_context, impl_decl_node);
+// TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry;
+// resolve_struct_type(g, import, type_entry);
+// return resolve_expr_const_val_as_type(g, node, type_entry, false);
+//}
+//
+//static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+//
+// if (node->data.fn_call_expr.is_builtin) {
+// zig_panic("moved builtin fn call code to ir.cpp");
+// }
+//
+// TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);
+// if (invoke_type_entry->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// // use constant expression evaluator to figure out the function at compile time.
+// // otherwise we treat this as a function pointer.
+// ConstExprValue *const_val = &get_resolved_expr(fn_ref_expr)->const_val;
+//
+// if (const_val->ok) {
+// if (invoke_type_entry->id == TypeTableEntryIdMetaType) {
+// zig_unreachable();
+// } else if (invoke_type_entry->id == TypeTableEntryIdFn) {
+// AstNode *struct_node;
+// if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
+// fn_ref_expr->data.field_access_expr.is_member_fn)
+// {
+// struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
+// } else {
+// struct_node = nullptr;
+// }
+//
+// FnTableEntry *fn_table_entry = const_val->data.x_fn;
+// node->data.fn_call_expr.fn_entry = fn_table_entry;
+// return analyze_fn_call_ptr(g, import, context, expected_type, node,
+// fn_table_entry->type_entry, struct_node);
+// } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) {
+// TypeTableEntry *generic_fn_type = const_val->data.x_type;
+// AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node;
+// if (decl_node->type == NodeTypeFnProto) {
+// AstNode *struct_node;
+// if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
+// fn_ref_expr->data.field_access_expr.is_member_fn)
+// {
+// struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
+// } else {
+// struct_node = nullptr;
+// }
+//
+// FnTableEntry *fn_table_entry = decl_node->data.fn_proto.fn_table_entry;
+// if (fn_table_entry->proto_node->data.fn_proto.skip) {
+// return g->builtin_types.entry_invalid;
+// }
+// return analyze_fn_call_with_inline_args(g, import, context, expected_type, node,
+// fn_table_entry, struct_node);
+// } else {
+// return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type);
+// }
+// } else {
+// add_node_error(g, fn_ref_expr,
+// buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+//
+// // function pointer
+// if (invoke_type_entry->id == TypeTableEntryIdFn) {
+// return analyze_fn_call_ptr(g, import, context, expected_type, node, invoke_type_entry, nullptr);
+// } else {
+// add_node_error(g, fn_ref_expr,
+// buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//}
+//static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// AstNode **expr_node = &node->data.switch_expr.expr;
+// TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node);
+// ConstExprValue *expr_val = &get_resolved_expr(*expr_node)->const_val;
+// if (expr_val->ok && !expr_val->depends_on_compile_var) {
+// add_node_error(g, first_executing_node(*expr_node),
+// buf_sprintf("value is constant; unnecessary switch statement"));
+// }
+// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+//
+//
+// size_t prong_count = node->data.switch_expr.prongs.length;
+// AstNode **peer_nodes = allocate<AstNode*>(prong_count);
+// TypeTableEntry **peer_types = allocate<TypeTableEntry*>(prong_count);
+//
+// bool any_errors = false;
+// if (expr_type->id == TypeTableEntryIdInvalid) {
+// return expr_type;
+// } else if (expr_type->id == TypeTableEntryIdUnreachable) {
+// add_node_error(g, first_executing_node(*expr_node),
+// buf_sprintf("switch on unreachable expression not allowed"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+//
+// size_t *field_use_counts = nullptr;
+// HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes = {};
+// if (expr_type->id == TypeTableEntryIdEnum) {
+// field_use_counts = allocate<size_t>(expr_type->data.enumeration.src_field_count);
+// } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
+// err_use_nodes.init(10);
+// }
+//
+// size_t *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index;
+// *const_chosen_prong_index = SIZE_MAX;
+// AstNode *else_prong = nullptr;
+// for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
+// AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
+//
+// TypeTableEntry *var_type;
+// bool var_is_target_expr;
+// if (prong_node->data.switch_prong.items.length == 0) {
+// if (else_prong) {
+// add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression"));
+// any_errors = true;
+// } else {
+// else_prong = prong_node;
+// }
+// var_type = expr_type;
+// var_is_target_expr = true;
+// if (*const_chosen_prong_index == SIZE_MAX && expr_val->ok) {
+// *const_chosen_prong_index = prong_i;
+// }
+// } else {
+// bool all_agree_on_var_type = true;
+// var_type = nullptr;
+//
+// for (size_t item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {
+// AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
+// if (item_node->type == NodeTypeSwitchRange) {
+// zig_panic("TODO range in switch statement");
+// }
+//
+// if (expr_type->id == TypeTableEntryIdEnum) {
+// if (item_node->type == NodeTypeSymbol) {
+// Buf *field_name = item_node->data.symbol_expr.symbol;
+// TypeEnumField *type_enum_field = find_enum_type_field(expr_type, field_name);
+// if (type_enum_field) {
+// item_node->data.symbol_expr.enum_field = type_enum_field;
+// if (!var_type) {
+// var_type = type_enum_field->type_entry;
+// }
+// if (type_enum_field->type_entry != var_type) {
+// all_agree_on_var_type = false;
+// }
+// uint32_t field_index = type_enum_field->value;
+// assert(field_use_counts);
+// field_use_counts[field_index] += 1;
+// if (field_use_counts[field_index] > 1) {
+// add_node_error(g, item_node,
+// buf_sprintf("duplicate switch value: '%s'",
+// buf_ptr(type_enum_field->name)));
+// any_errors = true;
+// }
+// if (!any_errors && expr_val->ok) {
+// if (expr_val->data.x_enum.tag == type_enum_field->value) {
+// *const_chosen_prong_index = prong_i;
+// }
+// }
+// } else {
+// add_node_error(g, item_node,
+// buf_sprintf("enum '%s' has no field '%s'",
+// buf_ptr(&expr_type->name), buf_ptr(field_name)));
+// any_errors = true;
+// }
+// } else {
+// add_node_error(g, item_node, buf_sprintf("expected enum tag name"));
+// any_errors = true;
+// }
+// } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
+// if (item_node->type == NodeTypeSymbol) {
+// Buf *err_name = item_node->data.symbol_expr.symbol;
+// bool is_ok_case = buf_eql_str(err_name, "Ok");
+// auto err_table_entry = is_ok_case ? nullptr: g->error_table.maybe_get(err_name);
+// if (is_ok_case || err_table_entry) {
+// uint32_t err_value = is_ok_case ? 0 : err_table_entry->value->value;
+// item_node->data.symbol_expr.err_value = err_value;
+// TypeTableEntry *this_var_type;
+// if (is_ok_case) {
+// this_var_type = expr_type->data.error.child_type;
+// } else {
+// this_var_type = g->builtin_types.entry_pure_error;
+// }
+// if (!var_type) {
+// var_type = this_var_type;
+// }
+// if (this_var_type != var_type) {
+// all_agree_on_var_type = false;
+// }
+//
+// // detect duplicate switch values
+// auto existing_entry = err_use_nodes.maybe_get(err_value);
+// if (existing_entry) {
+// add_node_error(g, existing_entry->value,
+// buf_sprintf("duplicate switch value: '%s'", buf_ptr(err_name)));
+// any_errors = true;
+// } else {
+// err_use_nodes.put(err_value, item_node);
+// }
+//
+// if (!any_errors && expr_val->ok) {
+// if (expr_val->data.x_err.err->value == err_value) {
+// *const_chosen_prong_index = prong_i;
+// }
+// }
+// } else {
+// add_node_error(g, item_node,
+// buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name)));
+// any_errors = true;
+// }
+// } else {
+// add_node_error(g, item_node, buf_sprintf("expected error value name"));
+// any_errors = true;
+// }
+// } else {
+// if (!any_errors && expr_val->ok) {
+// // note: there is now a function in eval.cpp for doing const expr comparison
+// zig_panic("TODO determine if const exprs are equal");
+// }
+// TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node);
+// if (item_type->id != TypeTableEntryIdInvalid) {
+// ConstExprValue *const_val = &get_resolved_expr(item_node)->const_val;
+// if (!const_val->ok) {
+// add_node_error(g, item_node,
+// buf_sprintf("unable to evaluate constant expression"));
+// any_errors = true;
+// }
+// }
+// }
+// }
+// if (!var_type || !all_agree_on_var_type) {
+// var_type = expr_type;
+// var_is_target_expr = true;
+// } else {
+// var_is_target_expr = false;
+// }
+// }
+//
+// BlockContext *child_context = new_block_context(node, context);
+// prong_node->data.switch_prong.block_context = child_context;
+// AstNode *var_node = prong_node->data.switch_prong.var_symbol;
+// if (var_node) {
+// assert(var_node->type == NodeTypeSymbol);
+// Buf *var_name = var_node->data.symbol_expr.symbol;
+// var_node->block_context = child_context;
+// prong_node->data.switch_prong.var = add_local_var(g, var_node, import,
+// child_context, var_name, var_type, true, nullptr);
+// prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr;
+// }
+// }
+//
+// for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
+// AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
+// BlockContext *child_context = prong_node->data.switch_prong.block_context;
+// child_context->codegen_excluded = expr_val->ok && (*const_chosen_prong_index != prong_i);
+//
+// if (child_context->codegen_excluded) {
+// peer_types[prong_i] = g->builtin_types.entry_unreachable;
+// } else {
+// peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,
+// prong_node->data.switch_prong.expr);
+// }
+// // This must go after the analyze_expression for
+// // prong_node->data.switch_prong.expr because of AST rewriting.
+// peer_nodes[prong_i] = prong_node->data.switch_prong.expr;
+// }
+//
+// if (expr_type->id == TypeTableEntryIdEnum && !else_prong) {
+// for (uint32_t i = 0; i < expr_type->data.enumeration.src_field_count; i += 1) {
+// if (field_use_counts[i] == 0) {
+// add_node_error(g, node,
+// buf_sprintf("enumeration value '%s' not handled in switch",
+// buf_ptr(expr_type->data.enumeration.fields[i].name)));
+// any_errors = true;
+// }
+// }
+// }
+//
+// if (any_errors) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (prong_count == 0) {
+// add_node_error(g, node, buf_sprintf("switch statement has no prongs"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node,
+// peer_nodes, peer_types, prong_count);
+//
+// if (expr_val->ok) {
+// assert(*const_chosen_prong_index != SIZE_MAX);
+//
+// *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val;
+// // the target expr depends on a compile var because we have an error on unnecessary
+// // switch statement, so the entire switch statement does too
+// const_val->depends_on_compile_var = true;
+//
+// if (!const_val->ok) {
+// return add_error_if_type_is_num_lit(g, result_type, node);
+// }
+// } else {
+// return add_error_if_type_is_num_lit(g, result_type, node);
+// }
+//
+// return result_type;
+//}
+//
+//static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// if (!node->data.return_expr.expr) {
+// node->data.return_expr.expr = create_ast_void_node(g, import, node);
+// normalize_parent_ptrs(node);
+// }
+//
+// TypeTableEntry *expected_return_type = get_return_type(context);
+//
+// switch (node->data.return_expr.kind) {
+// case ReturnKindUnconditional:
+// zig_panic("TODO moved to ir.cpp");
+// case ReturnKindError:
+// {
+// TypeTableEntry *expected_err_type;
+// if (expected_type) {
+// expected_err_type = get_error_type(g, expected_type);
+// } else {
+// expected_err_type = nullptr;
+// }
+// TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_err_type,
+// node->data.return_expr.expr);
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// } else if (resolved_type->id == TypeTableEntryIdErrorUnion) {
+// if (expected_return_type->id != TypeTableEntryIdErrorUnion &&
+// expected_return_type->id != TypeTableEntryIdPureError)
+// {
+// ErrorMsg *msg = add_node_error(g, node,
+// buf_sprintf("%%return statement in function with return type '%s'",
+// buf_ptr(&expected_return_type->name)));
+// AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
+// add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
+// }
+//
+// return resolved_type->data.error.child_type;
+// } else {
+// add_node_error(g, node->data.return_expr.expr,
+// buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// case ReturnKindMaybe:
+// {
+// TypeTableEntry *expected_maybe_type;
+// if (expected_type) {
+// expected_maybe_type = get_maybe_type(g, expected_type);
+// } else {
+// expected_maybe_type = nullptr;
+// }
+// TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type,
+// node->data.return_expr.expr);
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// } else if (resolved_type->id == TypeTableEntryIdMaybe) {
+// if (expected_return_type->id != TypeTableEntryIdMaybe) {
+// ErrorMsg *msg = add_node_error(g, node,
+// buf_sprintf("?return statement in function with return type '%s'",
+// buf_ptr(&expected_return_type->name)));
+// AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
+// add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
+// }
+//
+// return resolved_type->data.maybe.child_type;
+// } else {
+// add_node_error(g, node->data.return_expr.expr,
+// buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// }
+// zig_unreachable();
+//}
+//static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeGoto);
+//
+// FnTableEntry *fn_table_entry = context->fn_entry;
+// assert(fn_table_entry);
+//
+// fn_table_entry->goto_list.append(node);
+//
+// return g->builtin_types.entry_unreachable;
+//}
+//
+//static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// AstNode *field_access_node, AstNode *value_node, TypeTableEntry *enum_type, Buf *field_name,
+// AstNode *out_node)
+//{
+// assert(field_access_node->type == NodeTypeFieldAccessExpr);
+//
+// TypeEnumField *type_enum_field = find_enum_type_field(enum_type, field_name);
+// if (type_enum_field->type_entry->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// field_access_node->data.field_access_expr.type_enum_field = type_enum_field;
+//
+// if (type_enum_field) {
+// if (value_node) {
+// AstNode **value_node_ptr = value_node->parent_field;
+// TypeTableEntry *value_type = analyze_expression(g, import, context,
+// type_enum_field->type_entry, value_node);
+//
+// if (value_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// StructValExprCodeGen *codegen = &field_access_node->data.field_access_expr.resolved_struct_val_expr;
+// codegen->type_entry = enum_type;
+// codegen->source_node = field_access_node;
+//
+// ConstExprValue *value_const_val = &get_resolved_expr(*value_node_ptr)->const_val;
+// if (value_const_val->ok) {
+// ConstExprValue *const_val = &get_resolved_expr(out_node)->const_val;
+// const_val->ok = true;
+// const_val->data.x_enum.tag = type_enum_field->value;
+// const_val->data.x_enum.payload = value_const_val;
+// } else {
+// if (context->fn_entry) {
+// context->fn_entry->struct_val_expr_alloca_list.append(codegen);
+// } else {
+// add_node_error(g, *value_node_ptr, buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// } else if (type_enum_field->type_entry->id != TypeTableEntryIdVoid) {
+// add_node_error(g, field_access_node,
+// buf_sprintf("enum value '%s.%s' requires parameter of type '%s'",
+// buf_ptr(&enum_type->name),
+// buf_ptr(field_name),
+// buf_ptr(&type_enum_field->type_entry->name)));
+// } else {
+// Expr *expr = get_resolved_expr(out_node);
+// expr->const_val.ok = true;
+// expr->const_val.data.x_enum.tag = type_enum_field->value;
+// expr->const_val.data.x_enum.payload = nullptr;
+// }
+// } else {
+// add_node_error(g, field_access_node,
+// buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
+// buf_ptr(&enum_type->name)));
+// }
+// return enum_type;
+//}
+//
+//static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g,
+// TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
+//{
+// assert(node->type == NodeTypeFieldAccessExpr);
+// if (!is_slice(bare_struct_type)) {
+// BlockContext *container_block_context = get_container_block_context(bare_struct_type);
+// assert(container_block_context);
+// auto entry = container_block_context->decl_table.maybe_get(field_name);
+// AstNode *fn_decl_node = entry ? entry->value : nullptr;
+// if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
+// resolve_top_level_decl(g, fn_decl_node, false);
+// TopLevelDecl *tld = get_as_top_level_decl(fn_decl_node);
+// if (tld->resolution == TldResolutionInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// node->data.field_access_expr.is_member_fn = true;
+// FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry;
+// if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
+// return resolve_expr_const_val_as_generic_fn(g, node, fn_entry->type_entry, false);
+// } else {
+// return resolve_expr_const_val_as_fn(g, node, fn_entry, false);
+// }
+// }
+// }
+// add_node_error(g, node,
+// buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
+// return g->builtin_types.entry_invalid;
+//}
+//
+//static TypeTableEntry *analyze_container_member_access(CodeGen *g,
+// Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
+//{
+// TypeTableEntry *bare_type = container_ref_type(struct_type);
+// if (!type_is_complete(bare_type)) {
+// resolve_container_type(g, bare_type);
+// }
+//
+// node->data.field_access_expr.bare_container_type = bare_type;
+//
+// if (bare_type->id == TypeTableEntryIdStruct) {
+// node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_type, field_name);
+// if (node->data.field_access_expr.type_struct_field) {
+// return node->data.field_access_expr.type_struct_field->type_entry;
+// } else {
+// return analyze_container_member_access_inner(g, bare_type, field_name,
+// node, struct_type);
+// }
+// } else if (bare_type->id == TypeTableEntryIdEnum) {
+// node->data.field_access_expr.type_enum_field = find_enum_type_field(bare_type, field_name);
+// if (node->data.field_access_expr.type_enum_field) {
+// return node->data.field_access_expr.type_enum_field->type_entry;
+// } else {
+// return analyze_container_member_access_inner(g, bare_type, field_name,
+// node, struct_type);
+// }
+// } else if (bare_type->id == TypeTableEntryIdUnion) {
+// zig_panic("TODO");
+// } else {
+// zig_unreachable();
+// }
+//}
+//
+//static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// AstNode *node)
+//{
+// assert(node->type == NodeTypeSliceExpr);
+//
+// TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr,
+// node->data.slice_expr.array_ref_expr);
+//
+// TypeTableEntry *return_type;
+//
+// if (array_type->id == TypeTableEntryIdInvalid) {
+// return_type = g->builtin_types.entry_invalid;
+// } else if (array_type->id == TypeTableEntryIdArray) {
+// return_type = get_slice_type(g, array_type->data.array.child_type,
+// node->data.slice_expr.is_const);
+// } else if (array_type->id == TypeTableEntryIdPointer) {
+// return_type = get_slice_type(g, array_type->data.pointer.child_type,
+// node->data.slice_expr.is_const);
+// } else if (array_type->id == TypeTableEntryIdStruct &&
+// array_type->data.structure.is_slice)
+// {
+// return_type = get_slice_type(g,
+// array_type->data.structure.fields[0].type_entry->data.pointer.child_type,
+// node->data.slice_expr.is_const);
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
+// return_type = g->builtin_types.entry_invalid;
+// }
+//
+// if (return_type->id != TypeTableEntryIdInvalid) {
+// node->data.slice_expr.resolved_struct_val_expr.type_entry = return_type;
+// node->data.slice_expr.resolved_struct_val_expr.source_node = node;
+// context->fn_entry->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr);
+// }
+//
+// analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.start);
+//
+// if (node->data.slice_expr.end) {
+// analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.end);
+// }
+//
+// return return_type;
+//}
+//
+//static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// AstNode *node, LValPurpose purpose)
+//{
+// TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr,
+// node->data.array_access_expr.array_ref_expr);
+//
+// TypeTableEntry *return_type;
+//
+// if (array_type->id == TypeTableEntryIdInvalid) {
+// return_type = g->builtin_types.entry_invalid;
+// } else if (array_type->id == TypeTableEntryIdArray) {
+// if (array_type->data.array.len == 0) {
+// add_node_error(g, node, buf_sprintf("out of bounds array access"));
+// }
+// return_type = array_type->data.array.child_type;
+// } else if (array_type->id == TypeTableEntryIdPointer) {
+// if (array_type->data.pointer.is_const && purpose == LValPurposeAssign) {
+// add_node_error(g, node, buf_sprintf("cannot assign to constant"));
+// return g->builtin_types.entry_invalid;
+// }
+// return_type = array_type->data.pointer.child_type;
+// } else if (array_type->id == TypeTableEntryIdStruct &&
+// array_type->data.structure.is_slice)
+// {
+// TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry;
+// if (pointer_type->data.pointer.is_const && purpose == LValPurposeAssign) {
+// add_node_error(g, node, buf_sprintf("cannot assign to constant"));
+// return g->builtin_types.entry_invalid;
+// }
+// return_type = pointer_type->data.pointer.child_type;
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
+// return_type = g->builtin_types.entry_invalid;
+// }
+//
+// analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.array_access_expr.subscript);
+//
+// return return_type;
+//}
+//
+//static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// AstNode *node)
+//{
+// assert(node->type == NodeTypeBinOpExpr);
+// BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
+//
+// AstNode *op1 = node->data.bin_op_expr.op1;
+// AstNode *op2 = node->data.bin_op_expr.op2;
+// TypeTableEntry *op1_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op1);
+// TypeTableEntry *op2_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op2);
+//
+// if (op1_type->id == TypeTableEntryIdInvalid ||
+// op2_type->id == TypeTableEntryIdInvalid)
+// {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
+// ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
+// if (!op1_val->ok || !op2_val->ok) {
+// return g->builtin_types.entry_bool;
+// }
+//
+// ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
+// eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val);
+// return g->builtin_types.entry_bool;
+//}
+//
+//static TypeTableEntry *analyze_array_mult(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeBinOpExpr);
+// assert(node->data.bin_op_expr.bin_op == BinOpTypeArrayMult);
+//
+// AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
+// AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
+//
+// TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
+// TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
+//
+// if (op1_type->id == TypeTableEntryIdInvalid ||
+// op2_type->id == TypeTableEntryIdInvalid)
+// {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
+// ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
+//
+// AstNode *bad_node;
+// if (!op1_val->ok) {
+// bad_node = *op1;
+// } else if (!op2_val->ok) {
+// bad_node = *op2;
+// } else {
+// bad_node = nullptr;
+// }
+// if (bad_node) {
+// add_node_error(g, bad_node, buf_sprintf("array multiplication requires constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (op1_type->id != TypeTableEntryIdArray) {
+// add_node_error(g, *op1,
+// buf_sprintf("expected array type, got '%s'", buf_ptr(&op1_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (op2_type->id != TypeTableEntryIdNumLitInt &&
+// op2_type->id != TypeTableEntryIdInt)
+// {
+// add_node_error(g, *op2, buf_sprintf("expected integer type, got '%s'", buf_ptr(&op2_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (op2_val->data.x_bignum.is_negative) {
+// add_node_error(g, *op2, buf_sprintf("expected positive number"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+// const_val->ok = true;
+// const_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
+//
+// TypeTableEntry *child_type = op1_type->data.array.child_type;
+// BigNum old_array_len;
+// bignum_init_unsigned(&old_array_len, op1_type->data.array.len);
+//
+// BigNum new_array_len;
+// if (bignum_mul(&new_array_len, &old_array_len, &op2_val->data.x_bignum)) {
+// add_node_error(g, node, buf_sprintf("operation results in overflow"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// uint64_t old_array_len_bare = op1_type->data.array.len;
+// uint64_t operand_amt = op2_val->data.x_bignum.data.x_uint;
+//
+// uint64_t new_array_len_bare = new_array_len.data.x_uint;
+// const_val->data.x_array.fields = allocate<ConstExprValue*>(new_array_len_bare);
+//
+// uint64_t i = 0;
+// for (uint64_t x = 0; x < operand_amt; x += 1) {
+// for (uint64_t y = 0; y < old_array_len_bare; y += 1) {
+// const_val->data.x_array.fields[i] = op1_val->data.x_array.fields[y];
+// i += 1;
+// }
+// }
+//
+// return get_array_type(g, child_type, new_array_len_bare);
+//}
+//
+//static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import,
+// BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node)
+//{
+// AstNode *op1 = node->data.unwrap_err_expr.op1;
+// AstNode *op2 = node->data.unwrap_err_expr.op2;
+// AstNode *var_node = node->data.unwrap_err_expr.symbol;
+//
+// TypeTableEntry *lhs_type = analyze_expression(g, import, parent_context, nullptr, op1);
+// if (lhs_type->id == TypeTableEntryIdInvalid) {
+// return lhs_type;
+// } else if (lhs_type->id == TypeTableEntryIdErrorUnion) {
+// TypeTableEntry *child_type = lhs_type->data.error.child_type;
+// BlockContext *child_context;
+// if (var_node) {
+// child_context = new_block_context(node, parent_context);
+// var_node->block_context = child_context;
+// Buf *var_name = var_node->data.symbol_expr.symbol;
+// node->data.unwrap_err_expr.var = add_local_var(g, var_node, import, child_context, var_name,
+// g->builtin_types.entry_pure_error, true, nullptr);
+// } else {
+// child_context = parent_context;
+// }
+//
+// analyze_expression(g, import, child_context, child_type, op2);
+// return child_type;
+// } else {
+// add_node_error(g, op1,
+// buf_sprintf("expected error type, got '%s'", buf_ptr(&lhs_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//}
+//
+//
+//static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *source_node,
+// AstNodeVariableDeclaration *variable_declaration,
+// bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr)
+//{
+// bool is_const = variable_declaration->is_const;
+// bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport);
+// bool is_extern = variable_declaration->is_extern;
+//
+// TypeTableEntry *explicit_type = nullptr;
+// if (variable_declaration->type != nullptr) {
+// explicit_type = analyze_type_expr(g, import, context, variable_declaration->type);
+// if (explicit_type->id == TypeTableEntryIdUnreachable) {
+// add_node_error(g, variable_declaration->type,
+// buf_sprintf("variable of type 'unreachable' not allowed"));
+// explicit_type = g->builtin_types.entry_invalid;
+// }
+// }
+//
+// TypeTableEntry *implicit_type = nullptr;
+// if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) {
+// implicit_type = explicit_type;
+// } else if (variable_declaration->expr) {
+// implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
+// if (implicit_type->id == TypeTableEntryIdInvalid) {
+// // ignore the poison value
+// } else if (expr_is_maybe) {
+// if (implicit_type->id == TypeTableEntryIdMaybe) {
+// if (var_is_ptr) {
+// // TODO if the expression is constant, can't get pointer to it
+// implicit_type = get_pointer_to_type(g, implicit_type->data.maybe.child_type, false);
+// } else {
+// implicit_type = implicit_type->data.maybe.child_type;
+// }
+// } else {
+// add_node_error(g, variable_declaration->expr, buf_sprintf("expected maybe type"));
+// implicit_type = g->builtin_types.entry_invalid;
+// }
+// } else if (implicit_type->id == TypeTableEntryIdUnreachable) {
+// add_node_error(g, source_node,
+// buf_sprintf("variable initialization is unreachable"));
+// implicit_type = g->builtin_types.entry_invalid;
+// } else if ((!is_const || is_export) &&
+// (implicit_type->id == TypeTableEntryIdNumLitFloat ||
+// implicit_type->id == TypeTableEntryIdNumLitInt))
+// {
+// add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
+// implicit_type = g->builtin_types.entry_invalid;
+// } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
+// add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
+// implicit_type = g->builtin_types.entry_invalid;
+// }
+// if (implicit_type->id != TypeTableEntryIdInvalid && !context->fn_entry) {
+// ConstExprValue *const_val = &get_resolved_expr(variable_declaration->expr)->const_val;
+// if (!const_val->ok) {
+// add_node_error(g, first_executing_node(variable_declaration->expr),
+// buf_sprintf("global variable initializer requires constant expression"));
+// }
+// }
+// } else if (!is_extern) {
+// add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
+// implicit_type = g->builtin_types.entry_invalid;
+// }
+//
+// TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
+// assert(type != nullptr); // should have been caught by the parser
+//
+// VariableTableEntry *var = add_local_var(g, source_node, import, context,
+// variable_declaration->symbol, type, is_const,
+// expr_is_maybe ? nullptr : variable_declaration->expr);
+//
+// variable_declaration->variable = var;
+//
+// return var;
+//}
+//
+//static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, TypeTableEntry *expected_type, AstNode *node)
+//{
+// AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
+// return analyze_variable_declaration_raw(g, import, context, node, variable_declaration,
+// false, nullptr, false);
+//}
+//
+//static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// AstNode *size_node = node->data.array_type.size;
+//
+// TypeTableEntry *child_type = analyze_type_expr_pointer_only(g, import, context,
+// node->data.array_type.child_type, true);
+//
+// if (child_type->id == TypeTableEntryIdUnreachable) {
+// add_node_error(g, node, buf_create_from_str("array of unreachable not allowed"));
+// return g->builtin_types.entry_invalid;
+// } else if (child_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (size_node) {
+// child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type);
+// TypeTableEntry *size_type = analyze_expression(g, import, context,
+// g->builtin_types.entry_usize, size_node);
+// if (size_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
+// if (const_val->ok) {
+// if (const_val->data.x_bignum.is_negative) {
+// add_node_error(g, size_node,
+// buf_sprintf("array size %s is negative",
+// buf_ptr(bignum_to_buf(&const_val->data.x_bignum))));
+// return g->builtin_types.entry_invalid;
+// } else {
+// return resolve_expr_const_val_as_type(g, node,
+// get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint), false);
+// }
+// } else if (context->fn_entry) {
+// return resolve_expr_const_val_as_type(g, node,
+// get_slice_type(g, child_type, node->data.array_type.is_const), false);
+// } else {
+// add_node_error(g, first_executing_node(size_node),
+// buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+// } else {
+// TypeTableEntry *slice_type = get_slice_type(g, child_type, node->data.array_type.is_const);
+// return resolve_expr_const_val_as_type(g, node, slice_type, false);
+// }
+//}
+//
+//static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeWhileExpr);
+//
+// AstNode **condition_node = &node->data.while_expr.condition;
+// AstNode *while_body_node = node->data.while_expr.body;
+// AstNode **continue_expr_node = &node->data.while_expr.continue_expr;
+//
+// TypeTableEntry *condition_type = analyze_expression(g, import, context,
+// g->builtin_types.entry_bool, *condition_node);
+//
+// if (*continue_expr_node) {
+// analyze_expression(g, import, context, g->builtin_types.entry_void, *continue_expr_node);
+// }
+//
+// BlockContext *child_context = new_block_context(node, context);
+// child_context->parent_loop_node = node;
+//
+// analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node);
+//
+//
+// TypeTableEntry *expr_return_type = g->builtin_types.entry_void;
+//
+// if (condition_type->id == TypeTableEntryIdInvalid) {
+// expr_return_type = g->builtin_types.entry_invalid;
+// } else {
+// // if the condition is a simple constant expression and there are no break statements
+// // then the return type is unreachable
+// ConstExprValue *const_val = &get_resolved_expr(*condition_node)->const_val;
+// if (const_val->ok) {
+// if (const_val->data.x_bool) {
+// node->data.while_expr.condition_always_true = true;
+// if (!node->data.while_expr.contains_break) {
+// expr_return_type = g->builtin_types.entry_unreachable;
+// }
+// }
+// }
+// }
+//
+// return expr_return_type;
+//}
+//
+//static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeBreak);
+//
+// AstNode *loop_node = context->parent_loop_node;
+// if (loop_node) {
+// if (loop_node->type == NodeTypeWhileExpr) {
+// loop_node->data.while_expr.contains_break = true;
+// } else if (loop_node->type == NodeTypeForExpr) {
+// loop_node->data.for_expr.contains_break = true;
+// } else {
+// zig_unreachable();
+// }
+// } else {
+// add_node_error(g, node, buf_sprintf("'break' expression outside loop"));
+// }
+// return g->builtin_types.entry_unreachable;
+//}
+//
+//static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// AstNode *loop_node = context->parent_loop_node;
+// if (loop_node) {
+// if (loop_node->type == NodeTypeWhileExpr) {
+// loop_node->data.while_expr.contains_continue = true;
+// } else if (loop_node->type == NodeTypeForExpr) {
+// loop_node->data.for_expr.contains_continue = true;
+// } else {
+// zig_unreachable();
+// }
+// } else {
+// add_node_error(g, node, buf_sprintf("'continue' expression outside loop"));
+// }
+// return g->builtin_types.entry_unreachable;
+//}
+//
+//static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// if (!parent_context->fn_entry) {
+// add_node_error(g, node, buf_sprintf("defer expression outside function definition"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (!node->data.defer.expr) {
+// add_node_error(g, node, buf_sprintf("defer expects an expression"));
+// return g->builtin_types.entry_void;
+// }
+//
+// node->data.defer.child_block = new_block_context(node, parent_context);
+//
+// TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr,
+// node->data.defer.expr);
+// validate_voided_expr(g, node->data.defer.expr, resolved_type);
+//
+// return g->builtin_types.entry_void;
+//}
+//
+//static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// if (node->data.string_literal.c) {
+// return resolve_expr_const_val_as_c_string_lit(g, node, node->data.string_literal.buf);
+// } else {
+// return resolve_expr_const_val_as_string_lit(g, node, node->data.string_literal.buf);
+// }
+//}
+//
+//static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// BlockContext *child_context = new_block_context(node, parent_context);
+// node->data.block.child_block = child_context;
+// TypeTableEntry *return_type = g->builtin_types.entry_void;
+//
+// for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
+// AstNode *child = node->data.block.statements.at(i);
+// if (child->type == NodeTypeLabel) {
+// FnTableEntry *fn_table_entry = child_context->fn_entry;
+// assert(fn_table_entry);
+//
+// LabelTableEntry *label = allocate<LabelTableEntry>(1);
+// label->decl_node = child;
+// label->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable);
+//
+// child->block_context = child_context;
+// child->data.label.label_entry = label;
+// fn_table_entry->all_labels.append(label);
+//
+// child_context->label_table.put(child->data.label.name, label);
+//
+// return_type = g->builtin_types.entry_void;
+// continue;
+// }
+// if (return_type->id == TypeTableEntryIdUnreachable) {
+// if (is_node_void_expr(child)) {
+// // {unreachable;void;void} is allowed.
+// // ignore void statements once we enter unreachable land.
+// analyze_expression(g, import, child_context, g->builtin_types.entry_void, child);
+// continue;
+// }
+// add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code"));
+// break;
+// }
+// bool is_last = (i == node->data.block.statements.length - 1);
+// TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
+// return_type = analyze_expression(g, import, child_context, passed_expected_type, child);
+// if (child->type == NodeTypeDefer && return_type->id != TypeTableEntryIdInvalid) {
+// // defer starts a new block context
+// child_context = child->data.defer.child_block;
+// assert(child_context);
+// }
+// if (!is_last) {
+// validate_voided_expr(g, child, return_type);
+// }
+// }
+// node->data.block.nested_block = child_context;
+//
+// ConstExprValue *const_val = &node->data.block.resolved_expr.const_val;
+// if (node->data.block.statements.length == 0) {
+// const_val->ok = true;
+// } else if (node->data.block.statements.length == 1) {
+// AstNode *only_node = node->data.block.statements.at(0);
+// ConstExprValue *other_const_val = &get_resolved_expr(only_node)->const_val;
+// if (other_const_val->ok) {
+// *const_val = *other_const_val;
+// }
+// }
+//
+// return return_type;
+//}
+//
+//static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// mark_impure_fn(g, context, node);
+//
+// node->data.asm_expr.return_count = 0;
+// TypeTableEntry *return_type = g->builtin_types.entry_void;
+// for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
+// AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
+// if (asm_output->return_type) {
+// node->data.asm_expr.return_count += 1;
+// return_type = analyze_type_expr(g, import, context, asm_output->return_type);
+// if (node->data.asm_expr.return_count > 1) {
+// add_node_error(g, node,
+// buf_sprintf("inline assembly allows up to one output value"));
+// break;
+// }
+// } else {
+// Buf *variable_name = asm_output->variable_name;
+// VariableTableEntry *var = find_variable(g, context, variable_name);
+// if (var) {
+// asm_output->variable = var;
+// return var->type;
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// }
+// for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
+// AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
+// analyze_expression(g, import, context, nullptr, asm_input->expr);
+// }
+//
+// return return_type;
+//}
+//
+//static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node, Buf *err_name)
+//{
+// auto err_table_entry = g->error_table.maybe_get(err_name);
+//
+// if (err_table_entry) {
+// return resolve_expr_const_val_as_err(g, node, err_table_entry->value);
+// }
+//
+// add_node_error(g, node,
+// buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name)));
+//
+// return g->builtin_types.entry_invalid;
+//}
+//
+//static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
+//{
+// Buf *variable_name = node->data.symbol_expr.symbol;
+//
+// auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name);
+// if (primitive_table_entry) {
+// return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value, false);
+// }
+//
+// VariableTableEntry *var = find_variable(g, context, variable_name);
+// if (var) {
+// TypeTableEntry *var_type = analyze_var_ref(g, node, var, context, false);
+// return var_type;
+// }
+//
+// AstNode *decl_node = find_decl(context, variable_name);
+// if (decl_node) {
+// return analyze_decl_ref(g, node, decl_node, pointer_only, context, false);
+// }
+//
+// if (import->any_imports_failed) {
+// // skip the error message since we had a failing import in this file
+// // if an import breaks we don't need 9999 undeclared identifier errors
+// return g->builtin_types.entry_invalid;
+// }
+//
+// mark_impure_fn(g, context, node);
+// add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
+// return g->builtin_types.entry_invalid;
+//}
+//
+//static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
+// bool pointer_only, BlockContext *block_context, bool depends_on_compile_var)
+//{
+// resolve_top_level_decl(g, decl_node, pointer_only);
+// TopLevelDecl *tld = get_as_top_level_decl(decl_node);
+// if (tld->resolution == TldResolutionInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (decl_node->type == NodeTypeVariableDeclaration) {
+// VariableTableEntry *var = decl_node->data.variable_declaration.variable;
+// return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var);
+// } else if (decl_node->type == NodeTypeFnProto) {
+// FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
+// assert(fn_entry->type_entry);
+// if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) {
+// return resolve_expr_const_val_as_generic_fn(g, source_node, fn_entry->type_entry, depends_on_compile_var);
+// } else {
+// return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var);
+// }
+// } else if (decl_node->type == NodeTypeContainerDecl) {
+// if (decl_node->data.struct_decl.generic_params.length > 0) {
+// TypeTableEntry *type_entry = decl_node->data.struct_decl.generic_fn_type;
+// assert(type_entry);
+// return resolve_expr_const_val_as_generic_fn(g, source_node, type_entry, depends_on_compile_var);
+// } else {
+// return resolve_expr_const_val_as_type(g, source_node, decl_node->data.struct_decl.type_entry,
+// depends_on_compile_var);
+// }
+// } else if (decl_node->type == NodeTypeTypeDecl) {
+// return resolve_expr_const_val_as_type(g, source_node, decl_node->data.type_decl.child_type_entry,
+// depends_on_compile_var);
+// } else {
+// zig_unreachable();
+// }
+//}
+//
+//static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var,
+// BlockContext *context, bool depends_on_compile_var)
+//{
+// get_resolved_expr(source_node)->variable = var;
+// if (!var_is_pure(var, context)) {
+// mark_impure_fn(g, context, source_node);
+// }
+// if (var->src_is_const && var->val_node) {
+// ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val;
+// if (other_const_val->ok) {
+// return resolve_expr_const_val_as_other_expr(g, source_node, var->val_node,
+// depends_on_compile_var || var->force_depends_on_compile_var);
+// }
+// }
+// return var->type;
+//}
+//
+//static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import,
+// BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeNullLiteral);
+//
+// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+// const_val->ok = true;
+//
+// return g->builtin_types.entry_null;
+//}
+//
+//static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// assert(node->type == NodeTypeUndefinedLiteral);
+//
+// Expr *expr = get_resolved_expr(node);
+// ConstExprValue *const_val = &expr->const_val;
+//
+// const_val->ok = true;
+// const_val->special = ConstValSpecialUndef;
+//
+// return expected_type ? expected_type : g->builtin_types.entry_undef;
+//}
+//
+//static TypeTableEntry *analyze_zeroes_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// Expr *expr = get_resolved_expr(node);
+// ConstExprValue *const_val = &expr->const_val;
+//
+// const_val->ok = true;
+// const_val->special = ConstValSpecialZeroes;
+//
+// return expected_type ? expected_type : g->builtin_types.entry_undef;
+//}
+//
+//static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import,
+// BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
+//{
+// return resolve_expr_const_val_as_bignum(g, node, expected_type, node->data.number_literal.bignum, false);
+//}
+//
+//static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+// TypeTableEntry *type_entry = analyze_fn_proto_type(g, import, context, expected_type, node,
+// false, false, nullptr);
+//
+// if (type_entry->id == TypeTableEntryIdInvalid) {
+// return type_entry;
+// }
+//
+// return resolve_expr_const_val_as_type(g, node, type_entry, false);
+//}
+//
+//static bool var_is_pure(VariableTableEntry *var, BlockContext *context) {
+// if (var->block_context->fn_entry == context->fn_entry) {
+// // variable was declared in the current function, so it's OK.
+// return true;
+// }
+// return var->src_is_const && var->type->deep_const;
+//}
+//
+//static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
+// if (type_entry->id == TypeTableEntryIdMetaType) {
+// add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type"));
+// } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
+// add_node_error(g, first_executing_node(source_node), buf_sprintf("statement ignores error value"));
+// }
+//}
+//
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 52535defe8..5a0f33b317 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -78,6 +78,12 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
fprintf(irp->f, "%s", buf_ptr(&fn_entry->symbol_name));
break;
}
+ case TypeTableEntryIdBlock:
+ {
+ AstNode *node = const_val->data.x_block->node;
+ fprintf(irp->f, "(scope:%zu:%zu)", node->line + 1, node->column + 1);
+ break;
+ }
case TypeTableEntryIdVar:
case TypeTableEntryIdFloat:
case TypeTableEntryIdArray:
@@ -91,7 +97,6 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
case TypeTableEntryIdUnion:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
- case TypeTableEntryIdBlock:
case TypeTableEntryIdGenericFn:
zig_panic("TODO render more constant types in IR printer");
}
@@ -263,18 +268,6 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
fprintf(irp->f, ")");
}
-static void ir_print_builtin_call(IrPrint *irp, IrInstructionBuiltinCall *call_instruction) {
- fprintf(irp->f, "@%s(", buf_ptr(&call_instruction->fn->name));
- for (size_t i = 0; i < call_instruction->fn->param_count; i += 1) {
- IrInstruction *arg = call_instruction->args[i];
- if (i != 0)
- fprintf(irp->f, ", ");
- ir_print_other_instruction(irp, arg);
- }
- fprintf(irp->f, ")");
-}
-
-
static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) {
const char *inline_kw = cond_br_instruction->is_inline ? "inline " : "";
fprintf(irp->f, "%sif (", inline_kw);
@@ -393,6 +386,14 @@ static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr
fprintf(irp->f, ")");
}
+static void ir_print_set_fn_test(IrPrint *irp, IrInstructionSetFnTest *instruction) {
+ fprintf(irp->f, "@setFnTest(");
+ ir_print_other_instruction(irp, instruction->fn_value);
+ fprintf(irp->f, ", ");
+ ir_print_other_instruction(irp, instruction->is_test);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@@ -425,9 +426,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdBr:
ir_print_br(irp, (IrInstructionBr *)instruction);
break;
- case IrInstructionIdBuiltinCall:
- ir_print_builtin_call(irp, (IrInstructionBuiltinCall *)instruction);
- break;
case IrInstructionIdPhi:
ir_print_phi(irp, (IrInstructionPhi *)instruction);
break;
@@ -470,6 +468,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdStructFieldPtr:
ir_print_struct_field_ptr(irp, (IrInstructionStructFieldPtr *)instruction);
break;
+ case IrInstructionIdSetFnTest:
+ ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction);
+ break;
case IrInstructionIdSwitchBr:
zig_panic("TODO print more IR instructions");
}
diff --git a/src/parseh.cpp b/src/parseh.cpp
index 1a581f3a23..f44875ff03 100644
--- a/src/parseh.cpp
+++ b/src/parseh.cpp
@@ -230,9 +230,7 @@ static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *chi
}
static AstNode *make_type_node(Context *c, TypeTableEntry *type_entry) {
- AstNode *node = create_node(c, NodeTypeSymbol);
- node->data.symbol_expr.override_type_entry = type_entry;
- return node;
+ zig_panic("TODO bypass AST in parseh");
}
static AstNode *create_fn_proto_node(Context *c, Buf *name, TypeTableEntry *fn_type) {