Commit ce3c52471d
Changed files (5)
src/all_types.hpp
@@ -1409,13 +1409,17 @@ enum AtomicOrder {
struct IrBasicBlock {
ZigList<IrInstruction *> instruction_list;
IrBasicBlock *other;
+ const char *name_hint;
+ size_t debug_id;
};
enum IrInstructionId {
IrInstructionIdInvalid,
+ IrInstructionIdBr,
IrInstructionIdCondBr,
IrInstructionIdSwitchBr,
IrInstructionIdPhi,
+ IrInstructionIdUnOp,
IrInstructionIdBinOp,
IrInstructionIdLoadVar,
IrInstructionIdStoreVar,
@@ -1442,9 +1446,15 @@ struct IrInstruction {
struct IrInstructionCondBr {
IrInstruction base;
- // If cond_inst_index == SIZE_MAX, then this is an unconditional branch.
- size_t cond_inst_index;
- size_t dest_basic_block_index;
+ IrInstruction *condition;
+ IrBasicBlock *then_block;
+ IrBasicBlock *else_block;
+};
+
+struct IrInstructionBr {
+ IrInstruction base;
+
+ IrBasicBlock *dest_block;
};
struct IrInstructionSwitchBrCase {
@@ -1464,11 +1474,35 @@ struct IrInstructionSwitchBr {
struct IrInstructionPhi {
IrInstruction base;
- size_t incoming_block_count;
+ size_t incoming_count;
IrBasicBlock **incoming_blocks;
IrInstruction **incoming_values;
};
+enum IrUnOp {
+ IrUnOpInvalid,
+ IrUnOpBoolNot,
+ IrUnOpBinNot,
+ IrUnOpNegation,
+ IrUnOpNegationWrap,
+ IrUnOpAddressOf,
+ IrUnOpConstAddressOf,
+ IrUnOpDereference,
+ IrUnOpError,
+ IrUnOpMaybe,
+ IrUnOpUnwrapError,
+ IrUnOpUnwrapMaybe,
+ IrUnOpErrorReturn,
+ IrUnOpMaybeReturn,
+};
+
+struct IrInstructionUnOp {
+ IrInstruction base;
+
+ IrUnOp op_id;
+ IrInstruction *value;
+};
+
enum IrBinOp {
IrBinOpInvalid,
IrBinOpBoolOr,
@@ -1529,8 +1563,7 @@ struct IrInstructionCall {
struct IrInstructionBuiltinCall {
IrInstruction base;
- BuiltinFnId fn_id;
- size_t arg_count;
+ BuiltinFnEntry *fn;
IrInstruction **args;
};
src/analyze.cpp
@@ -1067,76 +1067,6 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
return get_fn_type(g, &fn_type_id, gen_debug_info);
}
-static Buf *resolve_const_expr_str(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode **node) {
- TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true);
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *node);
-
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return nullptr;
- }
-
- ConstExprValue *const_str_val = &get_resolved_expr(*node)->const_val;
-
- if (!const_str_val->ok) {
- add_node_error(g, *node, buf_sprintf("unable to evaluate constant expression"));
- return nullptr;
- }
-
- ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0];
- uint64_t len = ptr_field->data.x_ptr.len;
- Buf *result = buf_alloc();
- for (uint64_t i = 0; i < len; i += 1) {
- ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i];
- uint64_t big_c = char_val->data.x_bignum.data.x_uint;
- assert(big_c <= UINT8_MAX);
- uint8_t c = big_c;
- buf_append_char(result, c);
- }
- return result;
-}
-
-static bool resolve_const_expr_bool(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode **node, bool *value)
-{
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *node);
-
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return false;
- }
-
- ConstExprValue *const_bool_val = &get_resolved_expr(*node)->const_val;
-
- if (!const_bool_val->ok) {
- add_node_error(g, *node, buf_sprintf("unable to evaluate constant expression"));
- return false;
- }
-
- *value = const_bool_val->data.x_bool;
- return true;
-}
-
-static FnTableEntry *resolve_const_expr_fn(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode **node)
-{
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr, *node);
-
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return nullptr;
- } else if (resolved_type->id == TypeTableEntryIdFn) {
- ConstExprValue *const_val = &get_resolved_expr(*node)->const_val;
-
- if (!const_val->ok) {
- add_node_error(g, *node, buf_sprintf("unable to evaluate constant expression"));
- return nullptr;
- }
-
- return const_val->data.x_fn;
- } else {
- add_node_error(g, *node, buf_sprintf("expected function, got '%s'", buf_ptr(&resolved_type->name)));
- return nullptr;
- }
-}
-
static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry,
ImportTableEntry *import, BlockContext *containing_context)
{
@@ -3073,16 +3003,6 @@ static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node,
return g->builtin_types.entry_bool;
}
-static TypeTableEntry *resolve_expr_const_val_as_non_null(CodeGen *g, AstNode *node,
- TypeTableEntry *type, ConstExprValue *other_val)
-{
- assert(other_val->ok);
- Expr *expr = get_resolved_expr(node);
- expr->const_val.ok = true;
- expr->const_val.data.x_maybe = other_val;
- return type;
-}
-
static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNode *node, Buf *str) {
Expr *expr = get_resolved_expr(node);
expr->const_val.ok = true;
@@ -4432,33 +4352,6 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import,
node, then_node, else_node, cond_is_const, cond_bool_val);
}
-static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode *node, const char *err_format, bool is_max)
-{
- assert(node->type == NodeTypeFnCallExpr);
- assert(node->data.fn_call_expr.params.length == 1);
-
- AstNode *type_node = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
-
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (type_entry->id == TypeTableEntryIdInt) {
- eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
- return g->builtin_types.entry_num_lit_int;
- } else if (type_entry->id == TypeTableEntryIdFloat) {
- eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
- return g->builtin_types.entry_num_lit_float;
- } else if (type_entry->id == TypeTableEntryIdBool) {
- eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
- return type_entry;
- } else {
- add_node_error(g, node,
- buf_sprintf(err_format, buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- }
-}
-
bool type_is_codegen_pointer(TypeTableEntry *type) {
if (type->id == TypeTableEntryIdPointer) return true;
if (type->id == TypeTableEntryIdFn) return true;
@@ -4469,1185 +4362,205 @@ bool type_is_codegen_pointer(TypeTableEntry *type) {
return false;
}
-static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- AstNode *node)
+static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type,
+ TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry)
{
- assert(node->type == NodeTypeFnCallExpr);
-
- if (context->fn_entry) {
- add_node_error(g, node, buf_sprintf("@import invalid inside function bodies"));
- return g->builtin_types.entry_invalid;
+ 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;
+}
- AstNode *first_param_node = node->data.fn_call_expr.params.at(0);
- Buf *import_target_str = resolve_const_expr_str(g, import, context, first_param_node->parent_field);
- if (!import_target_str) {
- return g->builtin_types.entry_invalid;
- }
+// 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);
- Buf *import_target_path;
- Buf *search_dir;
- assert(import->package);
- PackageTableEntry *target_package;
- auto package_entry = import->package->package_table.maybe_get(import_target_str);
- if (package_entry) {
- target_package = package_entry->value;
- import_target_path = &target_package->root_src_path;
- search_dir = &target_package->root_src_dir;
- } else {
- // try it as a filename
- target_package = import->package;
- import_target_path = import_target_str;
- search_dir = &import->package->root_src_dir;
+ if (fn_type->id == TypeTableEntryIdInvalid) {
+ return fn_type;
}
- Buf full_path = BUF_INIT;
- os_path_join(search_dir, import_target_path, &full_path);
+ // 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;
- Buf *import_code = buf_alloc();
- Buf *abs_full_path = buf_alloc();
- int err;
- if ((err = os_path_real(&full_path, abs_full_path))) {
- if (err == ErrorFileNotFound) {
- add_node_error(g, node,
- buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
- return g->builtin_types.entry_invalid;
- } else {
- g->error_during_imports = true;
- add_node_error(g, node,
- buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
- return g->builtin_types.entry_invalid;
- }
- }
+ // 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;
- auto import_entry = g->import_table.maybe_get(abs_full_path);
- if (import_entry) {
- return resolve_expr_const_val_as_import(g, node, import_entry->value);
- }
+ bool ok_invocation = true;
- if ((err = os_fetch_file_path(abs_full_path, import_code))) {
- if (err == ErrorFileNotFound) {
- add_node_error(g, node,
- buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
- return g->builtin_types.entry_invalid;
- } else {
+ 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("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
- return g->builtin_types.entry_invalid;
+ 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));
}
- ImportTableEntry *target_import = add_source_file(g, target_package,
- abs_full_path, search_dir, import_target_path, import_code);
-
- scan_decls(g, target_import, target_import->block_context, target_import->root);
- return resolve_expr_const_val_as_import(g, node, target_import);
-}
+ bool all_args_const_expr = true;
-static TypeTableEntry *analyze_c_import(CodeGen *g, ImportTableEntry *parent_import,
- BlockContext *parent_context, AstNode *node)
-{
- assert(node->type == NodeTypeFnCallExpr);
+ 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;
+ }
- if (parent_context->fn_entry) {
- add_node_error(g, node, buf_sprintf("@c_import invalid inside function bodies"));
- return g->builtin_types.entry_invalid;
+ 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);
+ }
}
- AstNode *block_node = node->data.fn_call_expr.params.at(0);
+ // 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;
+ }
- BlockContext *child_context = new_block_context(node, parent_context);
- child_context->c_import_buf = buf_alloc();
+ FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[next_type_i];
+ next_type_i += 1;
- TypeTableEntry *resolved_type = analyze_expression(g, parent_import, child_context,
- g->builtin_types.entry_void, block_node);
+ 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;
+ }
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
+ ConstExprValue *const_arg_val = &get_resolved_expr(*param_node)->const_val;
+ if (!const_arg_val->ok) {
+ all_args_const_expr = false;
+ }
}
- find_libc_include_path(g);
-
- ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
- child_import->c_import_node = node;
-
- ZigList<ErrorMsg *> errors = {0};
+ TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
- int err;
- if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g, node))) {
- zig_panic("unable to parse h file: %s\n", err_str(err));
+ if (return_type->id == TypeTableEntryIdInvalid) {
+ return return_type;
}
- if (errors.length > 0) {
- ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed"));
- for (size_t i = 0; i < errors.length; i += 1) {
- ErrorMsg *err_msg = errors.at(i);
- err_msg_add_note(parent_err_msg, err_msg);
+ 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;
+ }
}
-
- return g->builtin_types.entry_invalid;
}
-
- if (g->verbose) {
- fprintf(stderr, "\nc_import:\n");
- fprintf(stderr, "-----------\n");
- ast_render(stderr, child_import->root, 4);
+ 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;
+ }
}
- child_import->di_file = parent_import->di_file;
- child_import->block_context = new_block_context(child_import->root, nullptr);
+ // 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"));
+ // }
+ //}
- scan_decls(g, child_import, child_import->block_context, child_import->root);
- return resolve_expr_const_val_as_import(g, node, child_import);
+ return return_type;
}
-static TypeTableEntry *analyze_err_name(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
+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(node->type == NodeTypeFnCallExpr);
-
- AstNode *err_value = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *resolved_type = analyze_expression(g, import, context,
- g->builtin_types.entry_pure_error, err_value);
-
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- }
-
- g->generate_error_name_table = true;
+ assert(call_node->type == NodeTypeFnCallExpr);
+ assert(fn_table_entry);
- TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true);
- return str_type;
-}
+ AstNode *decl_node = fn_table_entry->proto_node;
-static TypeTableEntry *analyze_embed_file(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
-{
- assert(node->type == NodeTypeFnCallExpr);
+ // 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;
- AstNode **first_param_node = &node->data.fn_call_expr.params.at(0);
- Buf *rel_file_path = resolve_const_expr_str(g, import, context, first_param_node);
- if (!rel_file_path) {
+ 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;
}
- // figure out absolute path to resource
- Buf source_dir_path = BUF_INIT;
- os_path_dirname(import->path, &source_dir_path);
+ 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;
- Buf file_path = BUF_INIT;
- os_path_resolve(&source_dir_path, rel_file_path, &file_path);
+ 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);
- // load from file system into const expr
- Buf file_contents = BUF_INIT;
- int err;
- if ((err = os_fetch_file_path(&file_path, &file_contents))) {
- if (err == ErrorFileNotFound) {
- add_node_error(g, node,
- buf_sprintf("unable to find '%s'", buf_ptr(&file_path)));
- return g->builtin_types.entry_invalid;
- } else {
- add_node_error(g, node,
- buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err)));
- return g->builtin_types.entry_invalid;
+ 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;
}
- }
- // TODO add dependency on the file we embedded so that we know if it changes
- // we'll have to invalidate the cache
-
- return resolve_expr_const_val_as_string_lit(g, node, &file_contents);
-}
-
-static TypeTableEntry *analyze_cmpxchg(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
-{
- assert(node->type == NodeTypeFnCallExpr);
-
- AstNode **ptr_arg = &node->data.fn_call_expr.params.at(0);
- AstNode **cmp_arg = &node->data.fn_call_expr.params.at(1);
- AstNode **new_arg = &node->data.fn_call_expr.params.at(2);
- AstNode **success_order_arg = &node->data.fn_call_expr.params.at(3);
- AstNode **failure_order_arg = &node->data.fn_call_expr.params.at(4);
-
- TypeTableEntry *ptr_type = analyze_expression(g, import, context, nullptr, *ptr_arg);
- if (ptr_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (ptr_type->id != TypeTableEntryIdPointer) {
- add_node_error(g, *ptr_arg,
- buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&ptr_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
- TypeTableEntry *cmp_type = analyze_expression(g, import, context, child_type, *cmp_arg);
- TypeTableEntry *new_type = analyze_expression(g, import, context, child_type, *new_arg);
-
- TypeTableEntry *success_order_type = analyze_expression(g, import, context,
- g->builtin_types.entry_atomic_order_enum, *success_order_arg);
- TypeTableEntry *failure_order_type = analyze_expression(g, import, context,
- g->builtin_types.entry_atomic_order_enum, *failure_order_arg);
-
- if (cmp_type->id == TypeTableEntryIdInvalid ||
- new_type->id == TypeTableEntryIdInvalid ||
- success_order_type->id == TypeTableEntryIdInvalid ||
- failure_order_type->id == TypeTableEntryIdInvalid)
- {
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *success_order_val = &get_resolved_expr(*success_order_arg)->const_val;
- ConstExprValue *failure_order_val = &get_resolved_expr(*failure_order_arg)->const_val;
- if (!success_order_val->ok) {
- add_node_error(g, *success_order_arg, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- } else if (!failure_order_val->ok) {
- add_node_error(g, *failure_order_arg, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- }
-
- if (success_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
- add_node_error(g, *success_order_arg,
- buf_sprintf("success atomic ordering must be Monotonic or stricter"));
- return g->builtin_types.entry_invalid;
- }
- if (failure_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
- add_node_error(g, *failure_order_arg,
- buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
- return g->builtin_types.entry_invalid;
- }
- if (failure_order_val->data.x_enum.tag > success_order_val->data.x_enum.tag) {
- add_node_error(g, *failure_order_arg,
- buf_sprintf("failure atomic ordering must be no stricter than success"));
- return g->builtin_types.entry_invalid;
- }
- if (failure_order_val->data.x_enum.tag == AtomicOrderRelease ||
- failure_order_val->data.x_enum.tag == AtomicOrderAcqRel)
- {
- add_node_error(g, *failure_order_arg,
- buf_sprintf("failure atomic ordering must not be Release or AcqRel"));
- return g->builtin_types.entry_invalid;
- }
-
- return g->builtin_types.entry_bool;
-}
-
-static TypeTableEntry *analyze_fence(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
-{
- assert(node->type == NodeTypeFnCallExpr);
-
- AstNode **atomic_order_arg = &node->data.fn_call_expr.params.at(0);
- TypeTableEntry *atomic_order_type = analyze_expression(g, import, context,
- g->builtin_types.entry_atomic_order_enum, *atomic_order_arg);
-
- if (atomic_order_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *atomic_order_val = &get_resolved_expr(*atomic_order_arg)->const_val;
-
- if (!atomic_order_val->ok) {
- add_node_error(g, *atomic_order_arg, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- }
-
- return g->builtin_types.entry_void;
-}
-
-static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
-{
- assert(node->type == NodeTypeFnCallExpr);
-
- AstNode **op1 = &node->data.fn_call_expr.params.at(0);
- AstNode **op2 = &node->data.fn_call_expr.params.at(1);
-
- TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
- TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
-
- AstNode *op_nodes[] = {*op1, *op2};
- TypeTableEntry *op_types[] = {op1_type, op2_type};
- TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node,
- op_nodes, op_types, 2);
-
- if (result_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (result_type->id == TypeTableEntryIdInt) {
- return result_type;
- } else if (result_type->id == TypeTableEntryIdNumLitInt) {
- // check for division by zero
- // check for non exact division
- zig_panic("TODO");
- } else {
- add_node_error(g, node,
- buf_sprintf("expected integer type, got '%s'", buf_ptr(&result_type->name)));
- return g->builtin_types.entry_invalid;
- }
-}
-
-static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
-{
- assert(node->type == NodeTypeFnCallExpr);
-
- AstNode **op1 = &node->data.fn_call_expr.params.at(0);
- AstNode **op2 = &node->data.fn_call_expr.params.at(1);
-
- TypeTableEntry *dest_type = analyze_type_expr(g, import, context, *op1);
- TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, *op2);
-
- if (dest_type->id == TypeTableEntryIdInvalid || src_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (dest_type->id != TypeTableEntryIdInt) {
- add_node_error(g, *op1,
- buf_sprintf("expected integer type, got '%s'", buf_ptr(&dest_type->name)));
- return g->builtin_types.entry_invalid;
- } else if (src_type->id != TypeTableEntryIdInt) {
- add_node_error(g, *op2,
- buf_sprintf("expected integer type, got '%s'", buf_ptr(&src_type->name)));
- return g->builtin_types.entry_invalid;
- } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
- const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned";
- add_node_error(g, *op2,
- buf_sprintf("expected %s integer type, got '%s'", sign_str, buf_ptr(&src_type->name)));
- return g->builtin_types.entry_invalid;
- } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) {
- add_node_error(g, *op2,
- buf_sprintf("type '%s' has same or fewer bits than destination type '%s'",
- buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- // TODO const expr eval
-
- return dest_type;
-}
-
-static TypeTableEntry *analyze_compile_err(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
-{
- AstNode *first_param_node = node->data.fn_call_expr.params.at(0);
- Buf *err_msg = resolve_const_expr_str(g, import, context, first_param_node->parent_field);
- if (!err_msg) {
- return g->builtin_types.entry_invalid;
- }
-
- add_node_error(g, node, err_msg);
-
- return g->builtin_types.entry_invalid;
-}
-
-static TypeTableEntry *analyze_int_type(CodeGen *g, ImportTableEntry *import,
- BlockContext *context, AstNode *node)
-{
- AstNode **is_signed_node = &node->data.fn_call_expr.params.at(0);
- AstNode **bit_count_node = &node->data.fn_call_expr.params.at(1);
-
- TypeTableEntry *bool_type = g->builtin_types.entry_bool;
- TypeTableEntry *usize_type = g->builtin_types.entry_usize;
- TypeTableEntry *is_signed_type = analyze_expression(g, import, context, bool_type, *is_signed_node);
- TypeTableEntry *bit_count_type = analyze_expression(g, import, context, usize_type, *bit_count_node);
-
- if (is_signed_type->id == TypeTableEntryIdInvalid ||
- bit_count_type->id == TypeTableEntryIdInvalid)
- {
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *is_signed_val = &get_resolved_expr(*is_signed_node)->const_val;
- ConstExprValue *bit_count_val = &get_resolved_expr(*bit_count_node)->const_val;
-
- AstNode *bad_node = nullptr;
- if (!is_signed_val->ok) {
- bad_node = *is_signed_node;
- } else if (!bit_count_val->ok) {
- bad_node = *bit_count_node;
- }
- if (bad_node) {
- add_node_error(g, bad_node, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- }
-
- bool depends_on_compile_var = is_signed_val->depends_on_compile_var || bit_count_val->depends_on_compile_var;
-
- TypeTableEntry *int_type = get_int_type(g, is_signed_val->data.x_bool,
- bit_count_val->data.x_bignum.data.x_uint);
- return resolve_expr_const_val_as_type(g, node, int_type, depends_on_compile_var);
-
-}
-
-static TypeTableEntry *analyze_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)
-{
- 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 is_noinline;
- bool ok = resolve_const_expr_bool(g, import, context, value_node, &is_noinline);
- if (!ok) {
- return g->builtin_types.entry_invalid;
- }
-
- if (fn_entry->fn_no_inline_set_node) {
- ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function no inline attribute set twice"));
- add_error_note(g, msg, fn_entry->fn_no_inline_set_node, buf_sprintf("first set here"));
- return g->builtin_types.entry_invalid;
- }
- fn_entry->fn_no_inline_set_node = node;
-
- if (fn_entry->fn_inline == FnInlineAlways) {
- add_node_error(g, node, buf_sprintf("function is both inline and noinline"));
- fn_entry->proto_node->data.fn_proto.skip = true;
- return g->builtin_types.entry_invalid;
- } else if (is_noinline) {
- fn_entry->fn_inline = FnInlineNever;
- }
-
- return g->builtin_types.entry_void;
-}
-
-static TypeTableEntry *analyze_set_fn_static_eval(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 want_static_eval;
- bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_static_eval);
- if (!ok) {
- return g->builtin_types.entry_invalid;
- }
-
- if (fn_entry->fn_static_eval_set_node) {
- ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function static eval attribute set twice"));
- add_error_note(g, msg, fn_entry->fn_static_eval_set_node, buf_sprintf("first set here"));
- return g->builtin_types.entry_invalid;
- }
- fn_entry->fn_static_eval_set_node = node;
-
- if (want_static_eval && !context->fn_entry->is_pure) {
- add_node_error(g, node, buf_sprintf("attribute appears too late within function"));
- return g->builtin_types.entry_invalid;
- }
-
- if (want_static_eval) {
- fn_entry->want_pure = WantPureTrue;
- fn_entry->want_pure_attr_node = node;
- } else {
- fn_entry->want_pure = WantPureFalse;
- fn_entry->is_pure = false;
- }
-
- return g->builtin_types.entry_void;
-}
-
-static TypeTableEntry *analyze_set_fn_visible(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 want_export;
- bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_export);
- if (!ok) {
- return g->builtin_types.entry_invalid;
- }
-
- if (fn_entry->fn_export_set_node) {
- ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function visibility set twice"));
- add_error_note(g, msg, fn_entry->fn_export_set_node, buf_sprintf("first set here"));
- return g->builtin_types.entry_invalid;
- }
- fn_entry->fn_export_set_node = node;
-
- AstNodeFnProto *fn_proto = &fn_entry->proto_node->data.fn_proto;
- if (fn_proto->top_level_decl.visib_mod != VisibModExport) {
- ErrorMsg *msg = add_node_error(g, node,
- buf_sprintf("function must be marked export to set function visibility"));
- add_error_note(g, msg, fn_entry->proto_node, buf_sprintf("function declared here"));
- return g->builtin_types.entry_void;
- }
- if (!want_export) {
- fn_proto->top_level_decl.visib_mod = VisibModPub;
- }
-
- return g->builtin_types.entry_void;
-}
-
-static TypeTableEntry *analyze_set_debug_safety(CodeGen *g, ImportTableEntry *import,
- BlockContext *parent_context, AstNode *node)
-{
- AstNode **target_node = &node->data.fn_call_expr.params.at(0);
- AstNode **value_node = &node->data.fn_call_expr.params.at(1);
-
- TypeTableEntry *target_type = analyze_expression(g, import, parent_context, nullptr, *target_node);
- BlockContext *target_context;
- ConstExprValue *const_val = &get_resolved_expr(*target_node)->const_val;
- if (target_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- }
- if (!const_val->ok) {
- add_node_error(g, *target_node, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- }
- if (target_type->id == TypeTableEntryIdBlock) {
- target_context = const_val->data.x_block;
- } else if (target_type->id == TypeTableEntryIdFn) {
- target_context = const_val->data.x_fn->fn_def_node->data.fn_def.block_context;
- } else if (target_type->id == TypeTableEntryIdMetaType) {
- TypeTableEntry *type_arg = const_val->data.x_type;
- if (type_arg->id == TypeTableEntryIdStruct) {
- target_context = type_arg->data.structure.block_context;
- } else if (type_arg->id == TypeTableEntryIdEnum) {
- target_context = type_arg->data.enumeration.block_context;
- } else if (type_arg->id == TypeTableEntryIdUnion) {
- target_context = type_arg->data.unionation.block_context;
- } else {
- add_node_error(g, *target_node,
- buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name)));
- return g->builtin_types.entry_invalid;
- }
- } else {
- add_node_error(g, *target_node,
- buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name)));
- return g->builtin_types.entry_invalid;
- }
-
- bool want_debug_safety;
- bool ok = resolve_const_expr_bool(g, import, parent_context, value_node, &want_debug_safety);
- if (!ok) {
- return g->builtin_types.entry_invalid;
- }
-
- if (target_context->safety_set_node) {
- ErrorMsg *msg = add_node_error(g, node, buf_sprintf("debug safety for scope set twice"));
- add_error_note(g, msg, target_context->safety_set_node, buf_sprintf("first set here"));
- return g->builtin_types.entry_invalid;
- }
- target_context->safety_set_node = node;
-
- target_context->safety_off = !want_debug_safety;
-
- return g->builtin_types.entry_void;
-}
-
-static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- assert(node->type == NodeTypeFnCallExpr);
-
- AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
- Buf *name = fn_ref_expr->data.symbol_expr.symbol;
-
- auto entry = g->builtin_fn_table.maybe_get(name);
-
- if (!entry) {
- add_node_error(g, node,
- buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
- return g->builtin_types.entry_invalid;
- }
-
- BuiltinFnEntry *builtin_fn = entry->value;
- size_t actual_param_count = node->data.fn_call_expr.params.length;
-
- node->data.fn_call_expr.builtin_fn = builtin_fn;
-
- if (builtin_fn->param_count != actual_param_count) {
- add_node_error(g, node,
- buf_sprintf("expected %zu arguments, got %zu",
- builtin_fn->param_count, actual_param_count));
- return g->builtin_types.entry_invalid;
- }
-
- builtin_fn->ref_count += 1;
-
- switch (builtin_fn->id) {
- case BuiltinFnIdInvalid:
- zig_unreachable();
- case BuiltinFnIdAddWithOverflow:
- case BuiltinFnIdSubWithOverflow:
- case BuiltinFnIdMulWithOverflow:
- case BuiltinFnIdShlWithOverflow:
- {
- AstNode *type_node = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
- if (int_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_bool;
- } else if (int_type->id == TypeTableEntryIdInt) {
- AstNode *op1_node = node->data.fn_call_expr.params.at(1);
- AstNode *op2_node = node->data.fn_call_expr.params.at(2);
- AstNode *result_node = node->data.fn_call_expr.params.at(3);
-
- analyze_expression(g, import, context, int_type, op1_node);
- analyze_expression(g, import, context, int_type, op2_node);
- analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false),
- result_node);
- } else {
- add_node_error(g, type_node,
- buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
- }
-
- // TODO constant expression evaluation
-
- return g->builtin_types.entry_bool;
- }
- case BuiltinFnIdMemcpy:
- {
- AstNode *dest_node = node->data.fn_call_expr.params.at(0);
- AstNode *src_node = node->data.fn_call_expr.params.at(1);
- AstNode *len_node = node->data.fn_call_expr.params.at(2);
- TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
- TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, src_node);
- analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
-
- if (dest_type->id != TypeTableEntryIdInvalid &&
- dest_type->id != TypeTableEntryIdPointer)
- {
- add_node_error(g, dest_node,
- buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
- }
-
- if (src_type->id != TypeTableEntryIdInvalid &&
- src_type->id != TypeTableEntryIdPointer)
- {
- add_node_error(g, src_node,
- buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&src_type->name)));
- }
-
- if (dest_type->id == TypeTableEntryIdPointer &&
- src_type->id == TypeTableEntryIdPointer)
- {
- uint64_t dest_align = get_memcpy_align(g, dest_type->data.pointer.child_type);
- uint64_t src_align = get_memcpy_align(g, src_type->data.pointer.child_type);
- if (dest_align != src_align) {
- add_node_error(g, dest_node, buf_sprintf(
- "misaligned memcpy, '%s' has alignment '%" PRIu64 ", '%s' has alignment %" PRIu64,
- buf_ptr(&dest_type->name), dest_align,
- buf_ptr(&src_type->name), src_align));
- }
- }
-
- return builtin_fn->return_type;
- }
- case BuiltinFnIdMemset:
- {
- AstNode *dest_node = node->data.fn_call_expr.params.at(0);
- AstNode *char_node = node->data.fn_call_expr.params.at(1);
- AstNode *len_node = node->data.fn_call_expr.params.at(2);
- TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
- analyze_expression(g, import, context, builtin_fn->param_types[1], char_node);
- analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
-
- if (dest_type->id != TypeTableEntryIdInvalid &&
- dest_type->id != TypeTableEntryIdPointer)
- {
- add_node_error(g, dest_node,
- buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
- }
-
- return builtin_fn->return_type;
- }
- case BuiltinFnIdSizeof:
- {
- AstNode *type_node = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (type_entry->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, first_executing_node(type_node),
- buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- } else {
- uint64_t size_in_bytes = type_size(g, type_entry);
- bool depends_on_compile_var = (type_entry == g->builtin_types.entry_usize ||
- type_entry == g->builtin_types.entry_isize);
- return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
- size_in_bytes, depends_on_compile_var);
- }
- }
- case BuiltinFnIdAlignof:
- {
- AstNode *type_node = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (type_entry->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, first_executing_node(type_node),
- buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- } else {
- uint64_t align_in_bytes = LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref);
- return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
- align_in_bytes, false);
- }
- }
- case BuiltinFnIdMaxValue:
- return analyze_min_max_value(g, import, context, node,
- "no max value available for type '%s'", true);
- case BuiltinFnIdMinValue:
- return analyze_min_max_value(g, import, context, node,
- "no min value available for type '%s'", false);
- case BuiltinFnIdMemberCount:
- {
- AstNode *type_node = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
-
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdEnum) {
- uint64_t value_count = type_entry->data.enumeration.src_field_count;
- return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
- value_count, false);
- } else {
- add_node_error(g, node,
- buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case BuiltinFnIdTypeof:
- {
- AstNode *expr_node = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
-
- switch (type_entry->id) {
- case TypeTableEntryIdInvalid:
- return type_entry;
- case TypeTableEntryIdNumLitFloat:
- case TypeTableEntryIdNumLitInt:
- case TypeTableEntryIdUndefLit:
- case TypeTableEntryIdNullLit:
- case TypeTableEntryIdNamespace:
- case TypeTableEntryIdBlock:
- case TypeTableEntryIdGenericFn:
- case TypeTableEntryIdVar:
- add_node_error(g, expr_node,
- buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- case TypeTableEntryIdMetaType:
- case TypeTableEntryIdVoid:
- case TypeTableEntryIdBool:
- case TypeTableEntryIdUnreachable:
- case TypeTableEntryIdInt:
- case TypeTableEntryIdFloat:
- case TypeTableEntryIdPointer:
- case TypeTableEntryIdArray:
- case TypeTableEntryIdStruct:
- case TypeTableEntryIdMaybe:
- case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
- case TypeTableEntryIdEnum:
- case TypeTableEntryIdUnion:
- case TypeTableEntryIdFn:
- case TypeTableEntryIdTypeDecl:
- return resolve_expr_const_val_as_type(g, node, type_entry, false);
- }
- }
- case BuiltinFnIdCInclude:
- {
- if (!context->c_import_buf) {
- add_node_error(g, node, buf_sprintf("@c_include valid only in c_import blocks"));
- return g->builtin_types.entry_invalid;
- }
-
- AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field;
- TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true);
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *str_node);
-
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- }
-
- ConstExprValue *const_str_val = &get_resolved_expr(*str_node)->const_val;
-
- if (!const_str_val->ok) {
- add_node_error(g, *str_node, buf_sprintf("@c_include requires constant expression"));
- return g->builtin_types.entry_void;
- }
-
- buf_appendf(context->c_import_buf, "#include <");
- ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0];
- uint64_t len = ptr_field->data.x_ptr.len;
- for (uint64_t i = 0; i < len; i += 1) {
- ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i];
- uint64_t big_c = char_val->data.x_bignum.data.x_uint;
- assert(big_c <= UINT8_MAX);
- uint8_t c = big_c;
- buf_append_char(context->c_import_buf, c);
- }
- buf_appendf(context->c_import_buf, ">\n");
-
- return g->builtin_types.entry_void;
- }
- case BuiltinFnIdCDefine:
- zig_panic("TODO");
- case BuiltinFnIdCUndef:
- zig_panic("TODO");
-
- case BuiltinFnIdCompileVar:
- {
- AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field;
-
- Buf *var_name = resolve_const_expr_str(g, import, context, str_node);
- if (!var_name) {
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
- const_val->ok = true;
- const_val->depends_on_compile_var = true;
-
- if (buf_eql_str(var_name, "is_big_endian")) {
- return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true);
- } else if (buf_eql_str(var_name, "is_release")) {
- return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true);
- } else if (buf_eql_str(var_name, "is_test")) {
- return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true);
- } else if (buf_eql_str(var_name, "os")) {
- const_val->data.x_enum.tag = g->target_os_index;
- return g->builtin_types.entry_os_enum;
- } else if (buf_eql_str(var_name, "arch")) {
- const_val->data.x_enum.tag = g->target_arch_index;
- return g->builtin_types.entry_arch_enum;
- } else if (buf_eql_str(var_name, "environ")) {
- const_val->data.x_enum.tag = g->target_environ_index;
- return g->builtin_types.entry_environ_enum;
- } else if (buf_eql_str(var_name, "object_format")) {
- const_val->data.x_enum.tag = g->target_oformat_index;
- return g->builtin_types.entry_oformat_enum;
- } else {
- add_node_error(g, *str_node,
- buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case BuiltinFnIdConstEval:
- {
- AstNode **expr_node = node->data.fn_call_expr.params.at(0)->parent_field;
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_type, *expr_node);
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- }
-
- ConstExprValue *const_expr_val = &get_resolved_expr(*expr_node)->const_val;
-
- if (!const_expr_val->ok) {
- add_node_error(g, *expr_node, buf_sprintf("unable to evaluate constant expression"));
- return g->builtin_types.entry_invalid;
- }
-
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
- *const_val = *const_expr_val;
-
- return resolved_type;
- }
- case BuiltinFnIdCtz:
- case BuiltinFnIdClz:
- {
- AstNode *type_node = node->data.fn_call_expr.params.at(0);
- TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
- if (int_type->id == TypeTableEntryIdInvalid) {
- return int_type;
- } else if (int_type->id == TypeTableEntryIdInt) {
- AstNode **expr_node = node->data.fn_call_expr.params.at(1)->parent_field;
- TypeTableEntry *resolved_type = analyze_expression(g, import, context, int_type, *expr_node);
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- }
-
- // TODO const expr eval
-
- return resolved_type;
- } else {
- add_node_error(g, type_node,
- buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case BuiltinFnIdImport:
- return analyze_import(g, import, context, node);
- case BuiltinFnIdCImport:
- return analyze_c_import(g, import, context, node);
- case BuiltinFnIdErrName:
- return analyze_err_name(g, import, context, node);
- case BuiltinFnIdBreakpoint:
- mark_impure_fn(g, context, node);
- return g->builtin_types.entry_void;
- case BuiltinFnIdReturnAddress:
- case BuiltinFnIdFrameAddress:
- mark_impure_fn(g, context, node);
- return builtin_fn->return_type;
- case BuiltinFnIdEmbedFile:
- return analyze_embed_file(g, import, context, node);
- case BuiltinFnIdCmpExchange:
- return analyze_cmpxchg(g, import, context, node);
- case BuiltinFnIdFence:
- return analyze_fence(g, import, context, node);
- case BuiltinFnIdDivExact:
- return analyze_div_exact(g, import, context, node);
- case BuiltinFnIdTruncate:
- return analyze_truncate(g, import, context, node);
- case BuiltinFnIdCompileErr:
- return analyze_compile_err(g, import, context, node);
- case BuiltinFnIdIntType:
- return analyze_int_type(g, import, context, node);
- case BuiltinFnIdUnreachable:
- return g->builtin_types.entry_unreachable;
- case BuiltinFnIdSetFnTest:
- return analyze_set_fn_test(g, import, context, node);
- case BuiltinFnIdSetFnNoInline:
- return analyze_set_fn_no_inline(g, import, context, node);
- case BuiltinFnIdSetFnStaticEval:
- return analyze_set_fn_static_eval(g, import, context, node);
- case BuiltinFnIdSetFnVisible:
- return analyze_set_fn_visible(g, import, context, node);
- case BuiltinFnIdSetDebugSafety:
- return analyze_set_debug_safety(g, import, context, node);
- }
- zig_unreachable();
-}
-
-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;
- }
+ 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);
@@ -5817,7 +4730,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
if (node->data.fn_call_expr.is_builtin) {
- return analyze_builtin_fn_call_expr(g, import, context, expected_type, node);
+ zig_panic("moved builtin fn call code to ir.cpp");
}
TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);
@@ -5885,222 +4798,6 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
}
}
-static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
- TypeTableEntry *expected_type, AstNode *node)
-{
- PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
- AstNode **expr_node = &node->data.prefix_op_expr.primary_expr;
- switch (prefix_op) {
- case PrefixOpInvalid:
- zig_unreachable();
- case PrefixOpBoolNot:
- {
- TypeTableEntry *type_entry = analyze_expression(g, import, context, g->builtin_types.entry_bool,
- *expr_node);
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_bool;
- }
-
- ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
- if (!target_const_val->ok) {
- return g->builtin_types.entry_bool;
- }
-
- bool answer = !target_const_val->data.x_bool;
- return resolve_expr_const_val_as_bool(g, node, answer, target_const_val->depends_on_compile_var);
- }
- case PrefixOpBinNot:
- {
- TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
- *expr_node);
- if (expr_type->id == TypeTableEntryIdInvalid) {
- return expr_type;
- } else if (expr_type->id == TypeTableEntryIdInt) {
- return expr_type;
- } else {
- add_node_error(g, node, buf_sprintf("unable to perform binary not operation on type '%s'",
- buf_ptr(&expr_type->name)));
- return g->builtin_types.entry_invalid;
- }
- // TODO const expr eval
- }
- case PrefixOpNegation:
- case PrefixOpNegationWrap:
- {
- TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node);
- if (expr_type->id == TypeTableEntryIdInvalid) {
- return expr_type;
- } else if ((expr_type->id == TypeTableEntryIdInt &&
- expr_type->data.integral.is_signed) ||
- expr_type->id == TypeTableEntryIdNumLitInt ||
- ((expr_type->id == TypeTableEntryIdFloat ||
- expr_type->id == TypeTableEntryIdNumLitFloat) &&
- prefix_op != PrefixOpNegationWrap))
- {
- ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
- if (!target_const_val->ok) {
- return expr_type;
- }
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
- const_val->ok = true;
- const_val->depends_on_compile_var = target_const_val->depends_on_compile_var;
- bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum);
- if (expr_type->id == TypeTableEntryIdFloat ||
- expr_type->id == TypeTableEntryIdNumLitFloat ||
- expr_type->id == TypeTableEntryIdNumLitInt)
- {
- return expr_type;
- }
-
- bool overflow = !bignum_fits_in_bits(&const_val->data.x_bignum,
- expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
- if (prefix_op == PrefixOpNegationWrap) {
- if (overflow) {
- const_val->data.x_bignum.is_negative = true;
- }
- } else if (overflow) {
- add_node_error(g, *expr_node, buf_sprintf("negation caused overflow"));
- return g->builtin_types.entry_invalid;
- }
- return expr_type;
- } else {
- const char *fmt = (prefix_op == PrefixOpNegationWrap) ?
- "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
- add_node_error(g, node, buf_sprintf(fmt, buf_ptr(&expr_type->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case PrefixOpAddressOf:
- case PrefixOpConstAddressOf:
- {
- bool is_const = (prefix_op == PrefixOpConstAddressOf);
-
- TypeTableEntry *child_type = analyze_lvalue(g, import, context,
- *expr_node, LValPurposeAddressOf, is_const);
-
- if (child_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (child_type->id == TypeTableEntryIdMetaType) {
- TypeTableEntry *meta_type = analyze_type_expr_pointer_only(g, import, context,
- *expr_node, true);
- if (meta_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (meta_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, node, buf_create_from_str("pointer to unreachable not allowed"));
- return g->builtin_types.entry_invalid;
- } else {
- return resolve_expr_const_val_as_type(g, node,
- get_pointer_to_type(g, meta_type, is_const), false);
- }
- } else if (child_type->id == TypeTableEntryIdNumLitInt ||
- child_type->id == TypeTableEntryIdNumLitFloat)
- {
- add_node_error(g, *expr_node,
- buf_sprintf("unable to get address of type '%s'", buf_ptr(&child_type->name)));
- return g->builtin_types.entry_invalid;
- } else {
- return get_pointer_to_type(g, child_type, is_const);
- }
- }
- case PrefixOpDereference:
- {
- TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdPointer) {
- return type_entry->data.pointer.child_type;
- } else {
- add_node_error(g, *expr_node,
- buf_sprintf("indirection requires pointer operand ('%s' invalid)",
- buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case PrefixOpMaybe:
- {
- TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
-
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdMetaType) {
- TypeTableEntry *meta_type = resolve_type(g, *expr_node);
- if (meta_type->id == TypeTableEntryIdInvalid) {
- return g->builtin_types.entry_invalid;
- } else if (meta_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in maybe type"));
- return g->builtin_types.entry_invalid;
- } else {
- return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type), false);
- }
- } else if (type_entry->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in maybe type"));
- return g->builtin_types.entry_invalid;
- } else {
- ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
- TypeTableEntry *maybe_type = get_maybe_type(g, type_entry);
- if (!target_const_val->ok) {
- return maybe_type;
- }
- return resolve_expr_const_val_as_non_null(g, node, maybe_type, target_const_val);
- }
- }
- case PrefixOpError:
- {
- TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
-
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdMetaType) {
- TypeTableEntry *meta_type = resolve_type(g, *expr_node);
- if (meta_type->id == TypeTableEntryIdInvalid) {
- return meta_type;
- } else if (meta_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in error type"));
- return g->builtin_types.entry_invalid;
- } else {
- return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type), false);
- }
- } else if (type_entry->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in error type"));
- return g->builtin_types.entry_invalid;
- } else {
- // TODO eval const expr
- return get_error_type(g, type_entry);
- }
-
- }
- case PrefixOpUnwrapError:
- {
- TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
-
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
- return type_entry->data.error.child_type;
- } else {
- add_node_error(g, *expr_node,
- buf_sprintf("expected error type, got '%s'", buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- case PrefixOpUnwrapMaybe:
- {
- TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
-
- if (type_entry->id == TypeTableEntryIdInvalid) {
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdMaybe) {
- return type_entry->data.maybe.child_type;
- } else {
- add_node_error(g, *expr_node,
- buf_sprintf("expected maybe type, got '%s'", buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- }
- }
- }
- zig_unreachable();
-}
-
static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@@ -6665,7 +5362,7 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn
return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only);
break;
case NodeTypePrefixOpExpr:
- return_type = analyze_prefix_op_expr(g, import, context, expected_type, node);
+ zig_panic("moved to ir.cpp");
break;
case NodeTypeIfBoolExpr:
return_type = analyze_if_bool_expr(g, import, context, expected_type, node);
src/codegen.cpp
@@ -2808,11 +2808,13 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdCast:
return ir_render_cast(g, executable, (IrInstructionCast *)instruction);
case IrInstructionIdCondBr:
+ case IrInstructionIdBr:
case IrInstructionIdSwitchBr:
case IrInstructionIdPhi:
case IrInstructionIdStoreVar:
case IrInstructionIdCall:
case IrInstructionIdBuiltinCall:
+ case IrInstructionIdUnOp:
zig_panic("TODO render more IR instructions to LLVM");
}
zig_unreachable();
src/ir.cpp
@@ -40,6 +40,14 @@ static size_t exec_next_debug_id(IrExecutable *exec) {
return result;
}
+static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, const char *name_hint) {
+ IrBasicBlock *result = allocate<IrBasicBlock>(1);
+ result->name_hint = name_hint;
+ result->debug_id = exec_next_debug_id(irb->exec);
+ irb->exec->basic_block_list.append(result);
+ return result;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) {
return IrInstructionIdCondBr;
}
@@ -52,6 +60,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPhi *) {
return IrInstructionIdPhi;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionUnOp *) {
+ return IrInstructionIdUnOp;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) {
return IrInstructionIdBinOp;
}
@@ -84,6 +96,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) {
return IrInstructionIdCast;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) {
+ return IrInstructionIdBr;
+}
+
template<typename T>
static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@@ -110,6 +126,18 @@ static IrInstruction *ir_build_cast(IrBuilder *irb, AstNode *source_node, IrInst
return &cast_instruction->base;
}
+static IrInstruction *ir_build_cond_br(IrBuilder *irb, AstNode *source_node, IrInstruction *condition,
+ IrBasicBlock *then_block, IrBasicBlock *else_block)
+{
+ IrInstructionCondBr *cond_br_instruction = ir_build_instruction<IrInstructionCondBr>(irb, source_node);
+ cond_br_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable;
+ cond_br_instruction->base.static_value.ok = true;
+ cond_br_instruction->condition = condition;
+ cond_br_instruction->then_block = then_block;
+ cond_br_instruction->else_block = else_block;
+ return &cond_br_instruction->base;
+}
+
static IrInstruction *ir_build_return(IrBuilder *irb, AstNode *source_node, IrInstruction *return_value) {
IrInstructionReturn *return_instruction = ir_build_instruction<IrInstructionReturn>(irb, source_node);
return_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable;
@@ -198,6 +226,38 @@ static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node,
return &call_instruction->base;
}
+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;
+ return &call_instruction->base;
+}
+
+static IrInstruction *ir_build_phi(IrBuilder *irb, AstNode *source_node,
+ size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values)
+{
+ IrInstructionPhi *phi_instruction = ir_build_instruction<IrInstructionPhi>(irb, source_node);
+ phi_instruction->incoming_count = incoming_count;
+ phi_instruction->incoming_blocks = incoming_blocks;
+ phi_instruction->incoming_values = incoming_values;
+ return &phi_instruction->base;
+}
+
+static IrInstruction *ir_build_br(IrBuilder *irb, AstNode *source_node, IrBasicBlock *dest_block) {
+ IrInstructionBr *br_instruction = ir_build_instruction<IrInstructionBr>(irb, source_node);
+ br_instruction->dest_block = dest_block;
+ return &br_instruction->base;
+}
+
+static IrInstruction *ir_build_un_op(IrBuilder *irb, AstNode *source_node, IrUnOp op_id, IrInstruction *value) {
+ IrInstructionUnOp *br_instruction = ir_build_instruction<IrInstructionUnOp>(irb, source_node);
+ br_instruction->op_id = op_id;
+ br_instruction->value = value;
+ return &br_instruction->base;
+}
+
//static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
// size_t result = 0;
@@ -246,6 +306,12 @@ static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, B
// return ir_build_return(irb, source_node, value);
//}
+static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
+ assert(basic_block);
+
+ irb->current_basic_block = basic_block;
+}
+
static IrInstruction *ir_gen_block(IrBuilder *irb, AstNode *block_node) {
assert(block_node->type == NodeTypeBlock);
@@ -433,15 +499,53 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, AstNode *node, bool pointer_
return irb->codegen->invalid_instruction;
}
-static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) {
+static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
- if (node->data.fn_call_expr.is_builtin) {
- zig_panic("TODO ir gen builtin fn");
+ AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+ Buf *name = fn_ref_expr->data.symbol_expr.symbol;
+ auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
+
+ if (!entry) {
+ add_node_error(irb->codegen, node,
+ buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
+ return irb->codegen->invalid_instruction;
+ }
+
+ BuiltinFnEntry *builtin_fn = entry->value;
+ size_t actual_param_count = node->data.fn_call_expr.params.length;
+
+ if (builtin_fn->param_count != actual_param_count) {
+ add_node_error(irb->codegen, node,
+ buf_sprintf("expected %zu arguments, got %zu",
+ builtin_fn->param_count, actual_param_count));
+ return irb->codegen->invalid_instruction;
+ }
+
+ builtin_fn->ref_count += 1;
+
+ 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;
}
+ return ir_build_builtin_call(irb, node, builtin_fn, args);
+}
+
+static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) {
+ assert(node->type == NodeTypeFnCallExpr);
+
+ if (node->data.fn_call_expr.is_builtin)
+ return ir_gen_builtin_fn_call(irb, node);
+
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
IrInstruction *fn = ir_gen_node(irb, fn_ref_node, node->block_context);
+ if (fn == irb->codegen->invalid_instruction)
+ return fn;
size_t arg_count = node->data.fn_call_expr.params.length;
IrInstruction **args = allocate<IrInstruction*>(arg_count);
@@ -453,9 +557,102 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) {
return ir_build_call(irb, node, fn, arg_count, args);
}
+static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, AstNode *node) {
+ assert(node->type == NodeTypeIfBoolExpr);
+
+ IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, node->block_context);
+ if (condition == irb->codegen->invalid_instruction)
+ return condition;
+
+ AstNode *then_node = node->data.if_bool_expr.then_block;
+ AstNode *else_node = node->data.if_bool_expr.else_node;
+
+ IrBasicBlock *then_block = ir_build_basic_block(irb, "Then");
+ IrBasicBlock *else_block = ir_build_basic_block(irb, "Else");
+ IrBasicBlock *endif_block = ir_build_basic_block(irb, "EndIf");
+
+ ir_build_cond_br(irb, condition->source_node, condition, then_block, else_block);
+
+ ir_set_cursor_at_end(irb, then_block);
+ IrInstruction *then_expr_result = ir_gen_node(irb, then_node, node->block_context);
+ if (then_expr_result == irb->codegen->invalid_instruction)
+ return then_expr_result;
+ IrBasicBlock *after_then_block = irb->current_basic_block;
+ ir_build_br(irb, node, endif_block);
+
+ ir_set_cursor_at_end(irb, else_block);
+ IrInstruction *else_expr_result;
+ if (else_node) {
+ else_expr_result = ir_gen_node(irb, else_node, node->block_context);
+ if (else_expr_result == irb->codegen->invalid_instruction)
+ return else_expr_result;
+ } else {
+ else_expr_result = ir_build_const_void(irb, node);
+ }
+ IrBasicBlock *after_else_block = irb->current_basic_block;
+ ir_build_br(irb, node, endif_block);
+
+ ir_set_cursor_at_end(irb, endif_block);
+ IrInstruction **incoming_values = allocate<IrInstruction *>(2);
+ incoming_values[0] = then_expr_result;
+ incoming_values[1] = else_expr_result;
+ IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+ incoming_blocks[0] = after_then_block;
+ incoming_blocks[1] = after_else_block;
+
+ return ir_build_phi(irb, node, 2, incoming_blocks, incoming_values);
+}
+
+static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, AstNode *node, IrUnOp op_id) {
+ assert(node->type == NodeTypePrefixOpExpr);
+ AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+
+ IrInstruction *value = ir_gen_node(irb, expr_node, node->block_context);
+ if (value == irb->codegen->invalid_instruction)
+ return value;
+
+ return ir_build_un_op(irb, node, op_id, value);
+}
+
+static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node) {
+ assert(node->type == NodeTypePrefixOpExpr);
+
+ PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
+ //AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+
+ switch (prefix_op) {
+ case PrefixOpInvalid:
+ zig_unreachable();
+ case PrefixOpBoolNot:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpBoolNot);
+ case PrefixOpBinNot:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpBinNot);
+ case PrefixOpNegation:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpNegation);
+ case PrefixOpNegationWrap:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpNegationWrap);
+ case PrefixOpAddressOf:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpAddressOf);
+ case PrefixOpConstAddressOf:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpConstAddressOf);
+ case PrefixOpDereference:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpDereference);
+ case PrefixOpMaybe:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpMaybe);
+ case PrefixOpError:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpError);
+ case PrefixOpUnwrapError:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapError);
+ case PrefixOpUnwrapMaybe:
+ return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapMaybe);
+ }
+ zig_unreachable();
+}
+
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
bool pointer_only)
{
+ assert(block_context);
node->block_context = block_context;
switch (node->type) {
@@ -469,15 +666,17 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
return ir_gen_symbol(irb, node, pointer_only);
case NodeTypeFnCallExpr:
return ir_gen_fn_call(irb, node);
+ case NodeTypeIfBoolExpr:
+ return ir_gen_if_bool_expr(irb, node);
+ case NodeTypePrefixOpExpr:
+ return ir_gen_prefix_op_expr(irb, node);
case NodeTypeUnwrapErrorExpr:
case NodeTypeReturnExpr:
case NodeTypeDefer:
case NodeTypeVariableDeclaration:
- case NodeTypePrefixOpExpr:
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeFieldAccessExpr:
- case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
@@ -533,8 +732,7 @@ static IrInstruction *ir_gen_add_return(CodeGen *g, AstNode *node, BlockContext
irb->codegen = g;
irb->exec = ir_executable;
- irb->current_basic_block = allocate<IrBasicBlock>(1);
- irb->exec->basic_block_list.append(irb->current_basic_block);
+ irb->current_basic_block = ir_build_basic_block(irb, "Entry");
IrInstruction *result = ir_gen_node_extra(irb, node, scope, pointer_only);
assert(result);
@@ -648,7 +846,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
const char *num_lit_str = (const_val->data.x_bignum.kind == BigNumKindFloat) ? "float" : "integer";
- add_node_error(ira->old_irb.codegen, instruction->source_node,
+ add_node_error(ira->codegen, instruction->source_node,
buf_sprintf("%s value %s cannot be implicitly casted to type '%s'",
num_lit_str,
buf_ptr(bignum_to_buf(&const_val->data.x_bignum)),
@@ -662,7 +860,7 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, IrInstruction *pa
assert(instruction_count >= 1);
IrInstruction *prev_inst = instructions[0];
if (prev_inst->type_entry->id == TypeTableEntryIdInvalid) {
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
}
for (size_t i = 1; i < instruction_count; i += 1) {
IrInstruction *cur_inst = instructions[i];
@@ -709,7 +907,7 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, IrInstruction *pa
prev_inst = cur_inst;
continue;
} else {
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
}
} else if (cur_type->id == TypeTableEntryIdNumLitInt ||
cur_type->id == TypeTableEntryIdNumLitFloat)
@@ -717,14 +915,14 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, IrInstruction *pa
if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type)) {
continue;
} else {
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
}
} else {
- add_node_error(ira->old_irb.codegen, parent_instruction->source_node,
+ add_node_error(ira->codegen, parent_instruction->source_node,
buf_sprintf("incompatible types: '%s' and '%s'",
buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
}
}
return prev_inst->type_entry;
@@ -1119,7 +1317,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type) {
assert(value);
- assert(value != ira->old_irb.codegen->invalid_instruction);
+ assert(value != ira->codegen->invalid_instruction);
assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid);
assert(value->type_entry);
assert(value->type_entry->id != TypeTableEntryIdInvalid);
@@ -1133,11 +1331,11 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, expected_type, value->type_entry, value);
switch (result) {
case ImplicitCastMatchResultNo:
- add_node_error(ira->old_irb.codegen, first_executing_node(value->source_node),
+ add_node_error(ira->codegen, first_executing_node(value->source_node),
buf_sprintf("expected type '%s', got '%s'",
buf_ptr(&expected_type->name),
buf_ptr(&value->type_entry->name)));
- return ira->old_irb.codegen->invalid_instruction;
+ return ira->codegen->invalid_instruction;
case ImplicitCastMatchResultYes:
{
@@ -1146,7 +1344,7 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
return cast_instruction;
}
case ImplicitCastMatchResultReportedError:
- return ira->old_irb.codegen->invalid_instruction;
+ return ira->codegen->invalid_instruction;
}
zig_unreachable();
@@ -1182,15 +1380,15 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
IrInstruction *op1 = bin_op_instruction->op1;
IrInstruction *op2 = bin_op_instruction->op2;
- TypeTableEntry *bool_type = ira->old_irb.codegen->builtin_types.entry_bool;
+ TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
IrInstruction *casted_op1 = ir_get_casted_value(ira, op1->other, bool_type);
- if (casted_op1 == ira->old_irb.codegen->invalid_instruction)
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ if (casted_op1 == ira->codegen->invalid_instruction)
+ return ira->codegen->builtin_types.entry_invalid;
IrInstruction *casted_op2 = ir_get_casted_value(ira, op2->other, bool_type);
- if (casted_op2 == ira->old_irb.codegen->invalid_instruction)
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ if (casted_op2 == ira->codegen->invalid_instruction)
+ return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *op1_val = &casted_op1->static_value;
ConstExprValue *op2_val = &casted_op2->static_value;
@@ -1232,7 +1430,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
AstNode *source_node = bin_op_instruction->base.source_node;
switch (resolved_type->id) {
case TypeTableEntryIdInvalid:
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
@@ -1251,17 +1449,17 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
case TypeTableEntryIdBlock:
case TypeTableEntryIdGenericFn:
if (!is_equality_cmp) {
- add_node_error(ira->old_irb.codegen, source_node,
+ add_node_error(ira->codegen, source_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
}
break;
case TypeTableEntryIdEnum:
if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) {
- add_node_error(ira->old_irb.codegen, source_node,
+ add_node_error(ira->codegen, source_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
}
break;
@@ -1273,9 +1471,9 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdUnion:
- add_node_error(ira->old_irb.codegen, source_node,
+ add_node_error(ira->codegen, source_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdVar:
zig_unreachable();
@@ -1286,7 +1484,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node,
op_id, op1->other, op2->other), &bin_op_instruction->base);
- return ira->old_irb.codegen->builtin_types.entry_bool;
+ return ira->codegen->builtin_types.entry_bool;
}
static uint64_t max_unsigned_val(TypeTableEntry *type_entry) {
@@ -1408,11 +1606,11 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
// float
} else {
AstNode *source_node = bin_op_instruction->base.source_node;
- add_node_error(ira->old_irb.codegen, source_node,
+ add_node_error(ira->codegen, source_node,
buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
buf_ptr(&op1->type_entry->name),
buf_ptr(&op2->type_entry->name)));
- return ira->old_irb.codegen->builtin_types.entry_invalid;
+ return ira->codegen->builtin_types.entry_invalid;
}
if (op1->static_value.ok && op2->static_value.ok) {
@@ -1525,6 +1723,1224 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
}
}
+static TypeTableEntry *ir_analyze_unary_bool_not(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
+ TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
+
+ IrInstruction *casted_value = ir_get_casted_value(ira, un_op_instruction->value, bool_type);
+ if (casted_value == ira->codegen->invalid_instruction)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *operand_val = &casted_value->static_value;
+ if (operand_val->ok) {
+ ConstExprValue *result_val = &un_op_instruction->base.static_value;
+ result_val->ok = true;
+ result_val->depends_on_compile_var = operand_val->depends_on_compile_var;
+ result_val->data.x_bool = !operand_val->data.x_bool;
+ return bool_type;
+ }
+
+ IrInstruction *new_instruction = ir_build_un_op(&ira->new_irb, un_op_instruction->base.source_node,
+ IrUnOpBoolNot, casted_value);
+ ir_link_new(new_instruction, &un_op_instruction->base);
+
+ return bool_type;
+}
+
+static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
+ IrUnOp op_id = un_op_instruction->op_id;
+ switch (op_id) {
+ case IrUnOpInvalid:
+ zig_unreachable();
+ case IrUnOpBoolNot:
+ return ir_analyze_unary_bool_not(ira, un_op_instruction);
+ zig_panic("TODO analyze PrefixOpBoolNot");
+ case IrUnOpBinNot:
+ zig_panic("TODO analyze PrefixOpBinNot");
+ //{
+ // TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
+ // *expr_node);
+ // if (expr_type->id == TypeTableEntryIdInvalid) {
+ // return expr_type;
+ // } else if (expr_type->id == TypeTableEntryIdInt) {
+ // return expr_type;
+ // } else {
+ // add_node_error(g, node, buf_sprintf("unable to perform binary not operation on type '%s'",
+ // buf_ptr(&expr_type->name)));
+ // return g->builtin_types.entry_invalid;
+ // }
+ // // TODO const expr eval
+ //}
+ case IrUnOpNegation:
+ case IrUnOpNegationWrap:
+ zig_panic("TODO analyze PrefixOpNegation[Wrap]");
+ //{
+ // TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node);
+ // if (expr_type->id == TypeTableEntryIdInvalid) {
+ // return expr_type;
+ // } else if ((expr_type->id == TypeTableEntryIdInt &&
+ // expr_type->data.integral.is_signed) ||
+ // expr_type->id == TypeTableEntryIdNumLitInt ||
+ // ((expr_type->id == TypeTableEntryIdFloat ||
+ // expr_type->id == TypeTableEntryIdNumLitFloat) &&
+ // prefix_op != PrefixOpNegationWrap))
+ // {
+ // ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
+ // if (!target_const_val->ok) {
+ // return expr_type;
+ // }
+ // ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+ // const_val->ok = true;
+ // const_val->depends_on_compile_var = target_const_val->depends_on_compile_var;
+ // bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum);
+ // if (expr_type->id == TypeTableEntryIdFloat ||
+ // expr_type->id == TypeTableEntryIdNumLitFloat ||
+ // expr_type->id == TypeTableEntryIdNumLitInt)
+ // {
+ // return expr_type;
+ // }
+
+ // bool overflow = !bignum_fits_in_bits(&const_val->data.x_bignum,
+ // expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
+ // if (prefix_op == PrefixOpNegationWrap) {
+ // if (overflow) {
+ // const_val->data.x_bignum.is_negative = true;
+ // }
+ // } else if (overflow) {
+ // add_node_error(g, *expr_node, buf_sprintf("negation caused overflow"));
+ // return g->builtin_types.entry_invalid;
+ // }
+ // return expr_type;
+ // } else {
+ // const char *fmt = (prefix_op == PrefixOpNegationWrap) ?
+ // "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
+ // add_node_error(g, node, buf_sprintf(fmt, buf_ptr(&expr_type->name)));
+ // return g->builtin_types.entry_invalid;
+ // }
+ //}
+ case IrUnOpAddressOf:
+ case IrUnOpConstAddressOf:
+ zig_panic("TODO analyze PrefixOpAddressOf and PrefixOpConstAddressOf");
+ //{
+ // bool is_const = (prefix_op == PrefixOpConstAddressOf);
+
+ // TypeTableEntry *child_type = analyze_lvalue(g, import, context,
+ // *expr_node, LValPurposeAddressOf, is_const);
+
+ // if (child_type->id == TypeTableEntryIdInvalid) {
+ // return g->builtin_types.entry_invalid;
+ // } else if (child_type->id == TypeTableEntryIdMetaType) {
+ // TypeTableEntry *meta_type = analyze_type_expr_pointer_only(g, import, context,
+ // *expr_node, true);
+ // if (meta_type->id == TypeTableEntryIdInvalid) {
+ // return g->builtin_types.entry_invalid;
+ // } else if (meta_type->id == TypeTableEntryIdUnreachable) {
+ // add_node_error(g, node, buf_create_from_str("pointer to unreachable not allowed"));
+ // return g->builtin_types.entry_invalid;
+ // } else {
+ // return resolve_expr_const_val_as_type(g, node,
+ // get_pointer_to_type(g, meta_type, is_const), false);
+ // }
+ // } else if (child_type->id == TypeTableEntryIdNumLitInt ||
+ // child_type->id == TypeTableEntryIdNumLitFloat)
+ // {
+ // add_node_error(g, *expr_node,
+ // buf_sprintf("unable to get address of type '%s'", buf_ptr(&child_type->name)));
+ // return g->builtin_types.entry_invalid;
+ // } else {
+ // return get_pointer_to_type(g, child_type, is_const);
+ // }
+ //}
+ case IrUnOpDereference:
+ zig_panic("TODO analyze PrefixOpDereference");
+ //{
+ // TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
+ // if (type_entry->id == TypeTableEntryIdInvalid) {
+ // return type_entry;
+ // } else if (type_entry->id == TypeTableEntryIdPointer) {
+ // return type_entry->data.pointer.child_type;
+ // } else {
+ // add_node_error(g, *expr_node,
+ // buf_sprintf("indirection requires pointer operand ('%s' invalid)",
+ // buf_ptr(&type_entry->name)));
+ // return g->builtin_types.entry_invalid;
+ // }
+ //}
+ case IrUnOpMaybe:
+ zig_panic("TODO analyze PrefixOpMaybe");
+ //{
+ // TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
+
+ // if (type_entry->id == TypeTableEntryIdInvalid) {
+ // return type_entry;
+ // } else if (type_entry->id == TypeTableEntryIdMetaType) {
+ // TypeTableEntry *meta_type = resolve_type(g, *expr_node);
+ // if (meta_type->id == TypeTableEntryIdInvalid) {
+ // return g->builtin_types.entry_invalid;
+ // } else if (meta_type->id == TypeTableEntryIdUnreachable) {
+ // add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in maybe type"));
+ // return g->builtin_types.entry_invalid;
+ // } else {
+ // return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type), false);
+ // }
+ // } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+ // add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in maybe type"));
+ // return g->builtin_types.entry_invalid;
+ // } else {
+ // ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
+ // TypeTableEntry *maybe_type = get_maybe_type(g, type_entry);
+ // if (!target_const_val->ok) {
+ // return maybe_type;
+ // }
+ // return resolve_expr_const_val_as_non_null(g, node, maybe_type, target_const_val);
+ // }
+ //}
+ case IrUnOpError:
+ zig_panic("TODO analyze PrefixOpError");
+ //{
+ // TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
+
+ // if (type_entry->id == TypeTableEntryIdInvalid) {
+ // return type_entry;
+ // } else if (type_entry->id == TypeTableEntryIdMetaType) {
+ // TypeTableEntry *meta_type = resolve_type(g, *expr_node);
+ // if (meta_type->id == TypeTableEntryIdInvalid) {
+ // return meta_type;
+ // } else if (meta_type->id == TypeTableEntryIdUnreachable) {
+ // add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in error type"));
+ // return g->builtin_types.entry_invalid;
+ // } else {
+ // return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type), false);
+ // }
+ // } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+ // add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in error type"));
+ // return g->builtin_types.entry_invalid;
+ // } else {
+ // // TODO eval const expr
+ // return get_error_type(g, type_entry);
+ // }
+
+ //}
+ case IrUnOpUnwrapError:
+ zig_panic("TODO analyze PrefixOpUnwrapError");
+ //{
+ // TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
+
+ // if (type_entry->id == TypeTableEntryIdInvalid) {
+ // return type_entry;
+ // } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
+ // return type_entry->data.error.child_type;
+ // } else {
+ // add_node_error(g, *expr_node,
+ // buf_sprintf("expected error type, got '%s'", buf_ptr(&type_entry->name)));
+ // return g->builtin_types.entry_invalid;
+ // }
+ //}
+ case IrUnOpUnwrapMaybe:
+ zig_panic("TODO analyze PrefixOpUnwrapMaybe");
+ //{
+ // TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
+
+ // if (type_entry->id == TypeTableEntryIdInvalid) {
+ // return type_entry;
+ // } else if (type_entry->id == TypeTableEntryIdMaybe) {
+ // return type_entry->data.maybe.child_type;
+ // } else {
+ // add_node_error(g, *expr_node,
+ // buf_sprintf("expected maybe type, got '%s'", buf_ptr(&type_entry->name)));
+ // return g->builtin_types.entry_invalid;
+ // }
+ //}
+ case IrUnOpErrorReturn:
+ zig_panic("TODO analyze IrUnOpErrorReturn");
+ case IrUnOpMaybeReturn:
+ zig_panic("TODO analyze IrUnOpMaybeReturn");
+ }
+ zig_unreachable();
+}
+
+//static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// AstNode *node, const char *err_format, bool is_max)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+// assert(node->data.fn_call_expr.params.length == 1);
+//
+// AstNode *type_node = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
+//
+// if (type_entry->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (type_entry->id == TypeTableEntryIdInt) {
+// eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
+// return g->builtin_types.entry_num_lit_int;
+// } else if (type_entry->id == TypeTableEntryIdFloat) {
+// eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
+// return g->builtin_types.entry_num_lit_float;
+// } else if (type_entry->id == TypeTableEntryIdBool) {
+// eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
+// return type_entry;
+// } else {
+// add_node_error(g, node,
+// buf_sprintf(err_format, buf_ptr(&type_entry->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//}
+
+//static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// if (context->fn_entry) {
+// add_node_error(g, node, buf_sprintf("@import invalid inside function bodies"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// AstNode *first_param_node = node->data.fn_call_expr.params.at(0);
+// Buf *import_target_str = resolve_const_expr_str(g, import, context, first_param_node->parent_field);
+// if (!import_target_str) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// Buf *import_target_path;
+// Buf *search_dir;
+// assert(import->package);
+// PackageTableEntry *target_package;
+// auto package_entry = import->package->package_table.maybe_get(import_target_str);
+// if (package_entry) {
+// target_package = package_entry->value;
+// import_target_path = &target_package->root_src_path;
+// search_dir = &target_package->root_src_dir;
+// } else {
+// // try it as a filename
+// target_package = import->package;
+// import_target_path = import_target_str;
+// search_dir = &import->package->root_src_dir;
+// }
+//
+// Buf full_path = BUF_INIT;
+// os_path_join(search_dir, import_target_path, &full_path);
+//
+// Buf *import_code = buf_alloc();
+// Buf *abs_full_path = buf_alloc();
+// int err;
+// if ((err = os_path_real(&full_path, abs_full_path))) {
+// if (err == ErrorFileNotFound) {
+// add_node_error(g, node,
+// buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
+// return g->builtin_types.entry_invalid;
+// } else {
+// g->error_during_imports = true;
+// add_node_error(g, node,
+// buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+//
+// auto import_entry = g->import_table.maybe_get(abs_full_path);
+// if (import_entry) {
+// return resolve_expr_const_val_as_import(g, node, import_entry->value);
+// }
+//
+// if ((err = os_fetch_file_path(abs_full_path, import_code))) {
+// if (err == ErrorFileNotFound) {
+// add_node_error(g, node,
+// buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
+// return g->builtin_types.entry_invalid;
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// ImportTableEntry *target_import = add_source_file(g, target_package,
+// abs_full_path, search_dir, import_target_path, import_code);
+//
+// scan_decls(g, target_import, target_import->block_context, target_import->root);
+//
+// return resolve_expr_const_val_as_import(g, node, target_import);
+//}
+//
+//static TypeTableEntry *analyze_c_import(CodeGen *g, ImportTableEntry *parent_import,
+// BlockContext *parent_context, AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// if (parent_context->fn_entry) {
+// add_node_error(g, node, buf_sprintf("@c_import invalid inside function bodies"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// AstNode *block_node = node->data.fn_call_expr.params.at(0);
+//
+// BlockContext *child_context = new_block_context(node, parent_context);
+// child_context->c_import_buf = buf_alloc();
+//
+// TypeTableEntry *resolved_type = analyze_expression(g, parent_import, child_context,
+// g->builtin_types.entry_void, block_node);
+//
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// }
+//
+// find_libc_include_path(g);
+//
+// ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
+// child_import->c_import_node = node;
+//
+// ZigList<ErrorMsg *> errors = {0};
+//
+// int err;
+// if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g, node))) {
+// zig_panic("unable to parse h file: %s\n", err_str(err));
+// }
+//
+// if (errors.length > 0) {
+// ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed"));
+// for (size_t i = 0; i < errors.length; i += 1) {
+// ErrorMsg *err_msg = errors.at(i);
+// err_msg_add_note(parent_err_msg, err_msg);
+// }
+//
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (g->verbose) {
+// fprintf(stderr, "\nc_import:\n");
+// fprintf(stderr, "-----------\n");
+// ast_render(stderr, child_import->root, 4);
+// }
+//
+// child_import->di_file = parent_import->di_file;
+// child_import->block_context = new_block_context(child_import->root, nullptr);
+//
+// scan_decls(g, child_import, child_import->block_context, child_import->root);
+// return resolve_expr_const_val_as_import(g, node, child_import);
+//}
+//
+//static TypeTableEntry *analyze_err_name(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// AstNode *err_value = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *resolved_type = analyze_expression(g, import, context,
+// g->builtin_types.entry_pure_error, err_value);
+//
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// }
+//
+// g->generate_error_name_table = true;
+//
+// TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true);
+// return str_type;
+//}
+//
+//static TypeTableEntry *analyze_embed_file(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// AstNode **first_param_node = &node->data.fn_call_expr.params.at(0);
+// Buf *rel_file_path = resolve_const_expr_str(g, import, context, first_param_node);
+// if (!rel_file_path) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// // figure out absolute path to resource
+// Buf source_dir_path = BUF_INIT;
+// os_path_dirname(import->path, &source_dir_path);
+//
+// Buf file_path = BUF_INIT;
+// os_path_resolve(&source_dir_path, rel_file_path, &file_path);
+//
+// // load from file system into const expr
+// Buf file_contents = BUF_INIT;
+// int err;
+// if ((err = os_fetch_file_path(&file_path, &file_contents))) {
+// if (err == ErrorFileNotFound) {
+// add_node_error(g, node,
+// buf_sprintf("unable to find '%s'", buf_ptr(&file_path)));
+// return g->builtin_types.entry_invalid;
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+//
+// // TODO add dependency on the file we embedded so that we know if it changes
+// // we'll have to invalidate the cache
+//
+// return resolve_expr_const_val_as_string_lit(g, node, &file_contents);
+//}
+//
+//static TypeTableEntry *analyze_cmpxchg(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// AstNode **ptr_arg = &node->data.fn_call_expr.params.at(0);
+// AstNode **cmp_arg = &node->data.fn_call_expr.params.at(1);
+// AstNode **new_arg = &node->data.fn_call_expr.params.at(2);
+// AstNode **success_order_arg = &node->data.fn_call_expr.params.at(3);
+// AstNode **failure_order_arg = &node->data.fn_call_expr.params.at(4);
+//
+// TypeTableEntry *ptr_type = analyze_expression(g, import, context, nullptr, *ptr_arg);
+// if (ptr_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (ptr_type->id != TypeTableEntryIdPointer) {
+// add_node_error(g, *ptr_arg,
+// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&ptr_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
+// TypeTableEntry *cmp_type = analyze_expression(g, import, context, child_type, *cmp_arg);
+// TypeTableEntry *new_type = analyze_expression(g, import, context, child_type, *new_arg);
+//
+// TypeTableEntry *success_order_type = analyze_expression(g, import, context,
+// g->builtin_types.entry_atomic_order_enum, *success_order_arg);
+// TypeTableEntry *failure_order_type = analyze_expression(g, import, context,
+// g->builtin_types.entry_atomic_order_enum, *failure_order_arg);
+//
+// if (cmp_type->id == TypeTableEntryIdInvalid ||
+// new_type->id == TypeTableEntryIdInvalid ||
+// success_order_type->id == TypeTableEntryIdInvalid ||
+// failure_order_type->id == TypeTableEntryIdInvalid)
+// {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *success_order_val = &get_resolved_expr(*success_order_arg)->const_val;
+// ConstExprValue *failure_order_val = &get_resolved_expr(*failure_order_arg)->const_val;
+// if (!success_order_val->ok) {
+// add_node_error(g, *success_order_arg, buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// } else if (!failure_order_val->ok) {
+// add_node_error(g, *failure_order_arg, buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (success_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
+// add_node_error(g, *success_order_arg,
+// buf_sprintf("success atomic ordering must be Monotonic or stricter"));
+// return g->builtin_types.entry_invalid;
+// }
+// if (failure_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
+// add_node_error(g, *failure_order_arg,
+// buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
+// return g->builtin_types.entry_invalid;
+// }
+// if (failure_order_val->data.x_enum.tag > success_order_val->data.x_enum.tag) {
+// add_node_error(g, *failure_order_arg,
+// buf_sprintf("failure atomic ordering must be no stricter than success"));
+// return g->builtin_types.entry_invalid;
+// }
+// if (failure_order_val->data.x_enum.tag == AtomicOrderRelease ||
+// failure_order_val->data.x_enum.tag == AtomicOrderAcqRel)
+// {
+// add_node_error(g, *failure_order_arg,
+// buf_sprintf("failure atomic ordering must not be Release or AcqRel"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// return g->builtin_types.entry_bool;
+//}
+//
+//static TypeTableEntry *analyze_fence(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// AstNode **atomic_order_arg = &node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *atomic_order_type = analyze_expression(g, import, context,
+// g->builtin_types.entry_atomic_order_enum, *atomic_order_arg);
+//
+// if (atomic_order_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *atomic_order_val = &get_resolved_expr(*atomic_order_arg)->const_val;
+//
+// if (!atomic_order_val->ok) {
+// add_node_error(g, *atomic_order_arg, buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// return g->builtin_types.entry_void;
+//}
+//
+//static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// AstNode **op1 = &node->data.fn_call_expr.params.at(0);
+// AstNode **op2 = &node->data.fn_call_expr.params.at(1);
+//
+// TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
+// TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
+//
+// AstNode *op_nodes[] = {*op1, *op2};
+// TypeTableEntry *op_types[] = {op1_type, op2_type};
+// TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node,
+// op_nodes, op_types, 2);
+//
+// if (result_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (result_type->id == TypeTableEntryIdInt) {
+// return result_type;
+// } else if (result_type->id == TypeTableEntryIdNumLitInt) {
+// // check for division by zero
+// // check for non exact division
+// zig_panic("TODO");
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("expected integer type, got '%s'", buf_ptr(&result_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//}
+//
+//static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// assert(node->type == NodeTypeFnCallExpr);
+//
+// AstNode **op1 = &node->data.fn_call_expr.params.at(0);
+// AstNode **op2 = &node->data.fn_call_expr.params.at(1);
+//
+// TypeTableEntry *dest_type = analyze_type_expr(g, import, context, *op1);
+// TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, *op2);
+//
+// if (dest_type->id == TypeTableEntryIdInvalid || src_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (dest_type->id != TypeTableEntryIdInt) {
+// add_node_error(g, *op1,
+// buf_sprintf("expected integer type, got '%s'", buf_ptr(&dest_type->name)));
+// return g->builtin_types.entry_invalid;
+// } else if (src_type->id != TypeTableEntryIdInt) {
+// add_node_error(g, *op2,
+// buf_sprintf("expected integer type, got '%s'", buf_ptr(&src_type->name)));
+// return g->builtin_types.entry_invalid;
+// } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
+// const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned";
+// add_node_error(g, *op2,
+// buf_sprintf("expected %s integer type, got '%s'", sign_str, buf_ptr(&src_type->name)));
+// return g->builtin_types.entry_invalid;
+// } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) {
+// add_node_error(g, *op2,
+// buf_sprintf("type '%s' has same or fewer bits than destination type '%s'",
+// buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// // TODO const expr eval
+//
+// return dest_type;
+//}
+//
+//static TypeTableEntry *analyze_compile_err(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// AstNode *first_param_node = node->data.fn_call_expr.params.at(0);
+// Buf *err_msg = resolve_const_expr_str(g, import, context, first_param_node->parent_field);
+// if (!err_msg) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// add_node_error(g, node, err_msg);
+//
+// return g->builtin_types.entry_invalid;
+//}
+//
+//static TypeTableEntry *analyze_int_type(CodeGen *g, ImportTableEntry *import,
+// BlockContext *context, AstNode *node)
+//{
+// AstNode **is_signed_node = &node->data.fn_call_expr.params.at(0);
+// AstNode **bit_count_node = &node->data.fn_call_expr.params.at(1);
+//
+// TypeTableEntry *bool_type = g->builtin_types.entry_bool;
+// TypeTableEntry *usize_type = g->builtin_types.entry_usize;
+// TypeTableEntry *is_signed_type = analyze_expression(g, import, context, bool_type, *is_signed_node);
+// TypeTableEntry *bit_count_type = analyze_expression(g, import, context, usize_type, *bit_count_node);
+//
+// if (is_signed_type->id == TypeTableEntryIdInvalid ||
+// bit_count_type->id == TypeTableEntryIdInvalid)
+// {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *is_signed_val = &get_resolved_expr(*is_signed_node)->const_val;
+// ConstExprValue *bit_count_val = &get_resolved_expr(*bit_count_node)->const_val;
+//
+// AstNode *bad_node = nullptr;
+// if (!is_signed_val->ok) {
+// bad_node = *is_signed_node;
+// } else if (!bit_count_val->ok) {
+// bad_node = *bit_count_node;
+// }
+// if (bad_node) {
+// add_node_error(g, bad_node, buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// bool depends_on_compile_var = is_signed_val->depends_on_compile_var || bit_count_val->depends_on_compile_var;
+//
+// TypeTableEntry *int_type = get_int_type(g, is_signed_val->data.x_bool,
+// bit_count_val->data.x_bignum.data.x_uint);
+// return resolve_expr_const_val_as_type(g, node, int_type, depends_on_compile_var);
+//
+//}
+//
+//static TypeTableEntry *analyze_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)
+//{
+// 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 is_noinline;
+// bool ok = resolve_const_expr_bool(g, import, context, value_node, &is_noinline);
+// if (!ok) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (fn_entry->fn_no_inline_set_node) {
+// ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function no inline attribute set twice"));
+// add_error_note(g, msg, fn_entry->fn_no_inline_set_node, buf_sprintf("first set here"));
+// return g->builtin_types.entry_invalid;
+// }
+// fn_entry->fn_no_inline_set_node = node;
+//
+// if (fn_entry->fn_inline == FnInlineAlways) {
+// add_node_error(g, node, buf_sprintf("function is both inline and noinline"));
+// fn_entry->proto_node->data.fn_proto.skip = true;
+// return g->builtin_types.entry_invalid;
+// } else if (is_noinline) {
+// fn_entry->fn_inline = FnInlineNever;
+// }
+//
+// return g->builtin_types.entry_void;
+//}
+//
+//static TypeTableEntry *analyze_set_fn_static_eval(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 want_static_eval;
+// bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_static_eval);
+// if (!ok) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (fn_entry->fn_static_eval_set_node) {
+// ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function static eval attribute set twice"));
+// add_error_note(g, msg, fn_entry->fn_static_eval_set_node, buf_sprintf("first set here"));
+// return g->builtin_types.entry_invalid;
+// }
+// fn_entry->fn_static_eval_set_node = node;
+//
+// if (want_static_eval && !context->fn_entry->is_pure) {
+// add_node_error(g, node, buf_sprintf("attribute appears too late within function"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (want_static_eval) {
+// fn_entry->want_pure = WantPureTrue;
+// fn_entry->want_pure_attr_node = node;
+// } else {
+// fn_entry->want_pure = WantPureFalse;
+// fn_entry->is_pure = false;
+// }
+//
+// return g->builtin_types.entry_void;
+//}
+//
+//static TypeTableEntry *analyze_set_fn_visible(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 want_export;
+// bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_export);
+// if (!ok) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (fn_entry->fn_export_set_node) {
+// ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function visibility set twice"));
+// add_error_note(g, msg, fn_entry->fn_export_set_node, buf_sprintf("first set here"));
+// return g->builtin_types.entry_invalid;
+// }
+// fn_entry->fn_export_set_node = node;
+//
+// AstNodeFnProto *fn_proto = &fn_entry->proto_node->data.fn_proto;
+// if (fn_proto->top_level_decl.visib_mod != VisibModExport) {
+// ErrorMsg *msg = add_node_error(g, node,
+// buf_sprintf("function must be marked export to set function visibility"));
+// add_error_note(g, msg, fn_entry->proto_node, buf_sprintf("function declared here"));
+// return g->builtin_types.entry_void;
+// }
+// if (!want_export) {
+// fn_proto->top_level_decl.visib_mod = VisibModPub;
+// }
+//
+// return g->builtin_types.entry_void;
+//}
+//
+//static TypeTableEntry *analyze_set_debug_safety(CodeGen *g, ImportTableEntry *import,
+// BlockContext *parent_context, AstNode *node)
+//{
+// AstNode **target_node = &node->data.fn_call_expr.params.at(0);
+// AstNode **value_node = &node->data.fn_call_expr.params.at(1);
+//
+// TypeTableEntry *target_type = analyze_expression(g, import, parent_context, nullptr, *target_node);
+// BlockContext *target_context;
+// ConstExprValue *const_val = &get_resolved_expr(*target_node)->const_val;
+// if (target_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// }
+// if (!const_val->ok) {
+// add_node_error(g, *target_node, buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+// if (target_type->id == TypeTableEntryIdBlock) {
+// target_context = const_val->data.x_block;
+// } else if (target_type->id == TypeTableEntryIdFn) {
+// target_context = const_val->data.x_fn->fn_def_node->data.fn_def.block_context;
+// } else if (target_type->id == TypeTableEntryIdMetaType) {
+// TypeTableEntry *type_arg = const_val->data.x_type;
+// if (type_arg->id == TypeTableEntryIdStruct) {
+// target_context = type_arg->data.structure.block_context;
+// } else if (type_arg->id == TypeTableEntryIdEnum) {
+// target_context = type_arg->data.enumeration.block_context;
+// } else if (type_arg->id == TypeTableEntryIdUnion) {
+// target_context = type_arg->data.unionation.block_context;
+// } else {
+// add_node_error(g, *target_node,
+// buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// } else {
+// add_node_error(g, *target_node,
+// buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// bool want_debug_safety;
+// bool ok = resolve_const_expr_bool(g, import, parent_context, value_node, &want_debug_safety);
+// if (!ok) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// if (target_context->safety_set_node) {
+// ErrorMsg *msg = add_node_error(g, node, buf_sprintf("debug safety for scope set twice"));
+// add_error_note(g, msg, target_context->safety_set_node, buf_sprintf("first set here"));
+// return g->builtin_types.entry_invalid;
+// }
+// target_context->safety_set_node = node;
+//
+// target_context->safety_off = !want_debug_safety;
+//
+// return g->builtin_types.entry_void;
+//}
+
+
+//static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// TypeTableEntry *expected_type, AstNode *node)
+//{
+//
+// switch (builtin_fn->id) {
+// case BuiltinFnIdInvalid:
+// zig_unreachable();
+// case BuiltinFnIdAddWithOverflow:
+// case BuiltinFnIdSubWithOverflow:
+// case BuiltinFnIdMulWithOverflow:
+// case BuiltinFnIdShlWithOverflow:
+// {
+// AstNode *type_node = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
+// if (int_type->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_bool;
+// } else if (int_type->id == TypeTableEntryIdInt) {
+// AstNode *op1_node = node->data.fn_call_expr.params.at(1);
+// AstNode *op2_node = node->data.fn_call_expr.params.at(2);
+// AstNode *result_node = node->data.fn_call_expr.params.at(3);
+//
+// analyze_expression(g, import, context, int_type, op1_node);
+// analyze_expression(g, import, context, int_type, op2_node);
+// analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false),
+// result_node);
+// } else {
+// add_node_error(g, type_node,
+// buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
+// }
+//
+// // TODO constant expression evaluation
+//
+// return g->builtin_types.entry_bool;
+// }
+// case BuiltinFnIdMemcpy:
+// {
+// AstNode *dest_node = node->data.fn_call_expr.params.at(0);
+// AstNode *src_node = node->data.fn_call_expr.params.at(1);
+// AstNode *len_node = node->data.fn_call_expr.params.at(2);
+// TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
+// TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, src_node);
+// analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
+//
+// if (dest_type->id != TypeTableEntryIdInvalid &&
+// dest_type->id != TypeTableEntryIdPointer)
+// {
+// add_node_error(g, dest_node,
+// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
+// }
+//
+// if (src_type->id != TypeTableEntryIdInvalid &&
+// src_type->id != TypeTableEntryIdPointer)
+// {
+// add_node_error(g, src_node,
+// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&src_type->name)));
+// }
+//
+// if (dest_type->id == TypeTableEntryIdPointer &&
+// src_type->id == TypeTableEntryIdPointer)
+// {
+// uint64_t dest_align = get_memcpy_align(g, dest_type->data.pointer.child_type);
+// uint64_t src_align = get_memcpy_align(g, src_type->data.pointer.child_type);
+// if (dest_align != src_align) {
+// add_node_error(g, dest_node, buf_sprintf(
+// "misaligned memcpy, '%s' has alignment '%" PRIu64 ", '%s' has alignment %" PRIu64,
+// buf_ptr(&dest_type->name), dest_align,
+// buf_ptr(&src_type->name), src_align));
+// }
+// }
+//
+// return builtin_fn->return_type;
+// }
+// case BuiltinFnIdMemset:
+// {
+// AstNode *dest_node = node->data.fn_call_expr.params.at(0);
+// AstNode *char_node = node->data.fn_call_expr.params.at(1);
+// AstNode *len_node = node->data.fn_call_expr.params.at(2);
+// TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
+// analyze_expression(g, import, context, builtin_fn->param_types[1], char_node);
+// analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
+//
+// if (dest_type->id != TypeTableEntryIdInvalid &&
+// dest_type->id != TypeTableEntryIdPointer)
+// {
+// add_node_error(g, dest_node,
+// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
+// }
+//
+// return builtin_fn->return_type;
+// }
+// case BuiltinFnIdSizeof:
+// {
+// AstNode *type_node = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
+// if (type_entry->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+// add_node_error(g, first_executing_node(type_node),
+// buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
+// return g->builtin_types.entry_invalid;
+// } else {
+// uint64_t size_in_bytes = type_size(g, type_entry);
+// bool depends_on_compile_var = (type_entry == g->builtin_types.entry_usize ||
+// type_entry == g->builtin_types.entry_isize);
+// return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
+// size_in_bytes, depends_on_compile_var);
+// }
+// }
+// case BuiltinFnIdAlignof:
+// {
+// AstNode *type_node = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
+// if (type_entry->id == TypeTableEntryIdInvalid) {
+// return g->builtin_types.entry_invalid;
+// } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+// add_node_error(g, first_executing_node(type_node),
+// buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name)));
+// return g->builtin_types.entry_invalid;
+// } else {
+// uint64_t align_in_bytes = LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref);
+// return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
+// align_in_bytes, false);
+// }
+// }
+// case BuiltinFnIdMaxValue:
+// return analyze_min_max_value(g, import, context, node,
+// "no max value available for type '%s'", true);
+// case BuiltinFnIdMinValue:
+// return analyze_min_max_value(g, import, context, node,
+// "no min value available for type '%s'", false);
+// case BuiltinFnIdMemberCount:
+// {
+// AstNode *type_node = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
+//
+// if (type_entry->id == TypeTableEntryIdInvalid) {
+// return type_entry;
+// } else if (type_entry->id == TypeTableEntryIdEnum) {
+// uint64_t value_count = type_entry->data.enumeration.src_field_count;
+// return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,
+// value_count, false);
+// } else {
+// add_node_error(g, node,
+// buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// case BuiltinFnIdTypeof:
+// {
+// AstNode *expr_node = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+//
+// switch (type_entry->id) {
+// case TypeTableEntryIdInvalid:
+// return type_entry;
+// case TypeTableEntryIdNumLitFloat:
+// case TypeTableEntryIdNumLitInt:
+// case TypeTableEntryIdUndefLit:
+// case TypeTableEntryIdNullLit:
+// case TypeTableEntryIdNamespace:
+// case TypeTableEntryIdBlock:
+// case TypeTableEntryIdGenericFn:
+// case TypeTableEntryIdVar:
+// add_node_error(g, expr_node,
+// buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name)));
+// return g->builtin_types.entry_invalid;
+// case TypeTableEntryIdMetaType:
+// case TypeTableEntryIdVoid:
+// case TypeTableEntryIdBool:
+// case TypeTableEntryIdUnreachable:
+// case TypeTableEntryIdInt:
+// case TypeTableEntryIdFloat:
+// case TypeTableEntryIdPointer:
+// case TypeTableEntryIdArray:
+// case TypeTableEntryIdStruct:
+// case TypeTableEntryIdMaybe:
+// case TypeTableEntryIdErrorUnion:
+// case TypeTableEntryIdPureError:
+// case TypeTableEntryIdEnum:
+// case TypeTableEntryIdUnion:
+// case TypeTableEntryIdFn:
+// case TypeTableEntryIdTypeDecl:
+// return resolve_expr_const_val_as_type(g, node, type_entry, false);
+// }
+// }
+// case BuiltinFnIdCInclude:
+// {
+// if (!context->c_import_buf) {
+// add_node_error(g, node, buf_sprintf("@c_include valid only in c_import blocks"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field;
+// TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true);
+// TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *str_node);
+//
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// }
+//
+// ConstExprValue *const_str_val = &get_resolved_expr(*str_node)->const_val;
+//
+// if (!const_str_val->ok) {
+// add_node_error(g, *str_node, buf_sprintf("@c_include requires constant expression"));
+// return g->builtin_types.entry_void;
+// }
+//
+// buf_appendf(context->c_import_buf, "#include <");
+// ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0];
+// uint64_t len = ptr_field->data.x_ptr.len;
+// for (uint64_t i = 0; i < len; i += 1) {
+// ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i];
+// uint64_t big_c = char_val->data.x_bignum.data.x_uint;
+// assert(big_c <= UINT8_MAX);
+// uint8_t c = big_c;
+// buf_append_char(context->c_import_buf, c);
+// }
+// buf_appendf(context->c_import_buf, ">\n");
+//
+// return g->builtin_types.entry_void;
+// }
+// case BuiltinFnIdCDefine:
+// zig_panic("TODO");
+// case BuiltinFnIdCUndef:
+// zig_panic("TODO");
+//
+// case BuiltinFnIdCompileVar:
+// {
+// AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field;
+//
+// Buf *var_name = resolve_const_expr_str(g, import, context, str_node);
+// if (!var_name) {
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+// const_val->ok = true;
+// const_val->depends_on_compile_var = true;
+//
+// if (buf_eql_str(var_name, "is_big_endian")) {
+// return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true);
+// } else if (buf_eql_str(var_name, "is_release")) {
+// return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true);
+// } else if (buf_eql_str(var_name, "is_test")) {
+// return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true);
+// } else if (buf_eql_str(var_name, "os")) {
+// const_val->data.x_enum.tag = g->target_os_index;
+// return g->builtin_types.entry_os_enum;
+// } else if (buf_eql_str(var_name, "arch")) {
+// const_val->data.x_enum.tag = g->target_arch_index;
+// return g->builtin_types.entry_arch_enum;
+// } else if (buf_eql_str(var_name, "environ")) {
+// const_val->data.x_enum.tag = g->target_environ_index;
+// return g->builtin_types.entry_environ_enum;
+// } else if (buf_eql_str(var_name, "object_format")) {
+// const_val->data.x_enum.tag = g->target_oformat_index;
+// return g->builtin_types.entry_oformat_enum;
+// } else {
+// add_node_error(g, *str_node,
+// buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// case BuiltinFnIdConstEval:
+// {
+// AstNode **expr_node = node->data.fn_call_expr.params.at(0)->parent_field;
+// TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_type, *expr_node);
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// }
+//
+// ConstExprValue *const_expr_val = &get_resolved_expr(*expr_node)->const_val;
+//
+// if (!const_expr_val->ok) {
+// add_node_error(g, *expr_node, buf_sprintf("unable to evaluate constant expression"));
+// return g->builtin_types.entry_invalid;
+// }
+//
+// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+// *const_val = *const_expr_val;
+//
+// return resolved_type;
+// }
+// case BuiltinFnIdCtz:
+// case BuiltinFnIdClz:
+// {
+// AstNode *type_node = node->data.fn_call_expr.params.at(0);
+// TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
+// if (int_type->id == TypeTableEntryIdInvalid) {
+// return int_type;
+// } else if (int_type->id == TypeTableEntryIdInt) {
+// AstNode **expr_node = node->data.fn_call_expr.params.at(1)->parent_field;
+// TypeTableEntry *resolved_type = analyze_expression(g, import, context, int_type, *expr_node);
+// if (resolved_type->id == TypeTableEntryIdInvalid) {
+// return resolved_type;
+// }
+//
+// // TODO const expr eval
+//
+// return resolved_type;
+// } else {
+// add_node_error(g, type_node,
+// buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
+// return g->builtin_types.entry_invalid;
+// }
+// }
+// case BuiltinFnIdImport:
+// return analyze_import(g, import, context, node);
+// case BuiltinFnIdCImport:
+// return analyze_c_import(g, import, context, node);
+// case BuiltinFnIdErrName:
+// return analyze_err_name(g, import, context, node);
+// case BuiltinFnIdBreakpoint:
+// mark_impure_fn(g, context, node);
+// return g->builtin_types.entry_void;
+// case BuiltinFnIdReturnAddress:
+// case BuiltinFnIdFrameAddress:
+// mark_impure_fn(g, context, node);
+// return builtin_fn->return_type;
+// case BuiltinFnIdEmbedFile:
+// return analyze_embed_file(g, import, context, node);
+// case BuiltinFnIdCmpExchange:
+// return analyze_cmpxchg(g, import, context, node);
+// case BuiltinFnIdFence:
+// return analyze_fence(g, import, context, node);
+// case BuiltinFnIdDivExact:
+// return analyze_div_exact(g, import, context, node);
+// case BuiltinFnIdTruncate:
+// return analyze_truncate(g, import, context, node);
+// case BuiltinFnIdCompileErr:
+// return analyze_compile_err(g, import, context, node);
+// case BuiltinFnIdIntType:
+// return analyze_int_type(g, import, context, node);
+// case BuiltinFnIdUnreachable:
+// return g->builtin_types.entry_unreachable;
+// case BuiltinFnIdSetFnTest:
+// return analyze_set_fn_test(g, import, context, node);
+// case BuiltinFnIdSetFnNoInline:
+// return analyze_set_fn_no_inline(g, import, context, node);
+// case BuiltinFnIdSetFnStaticEval:
+// return analyze_set_fn_static_eval(g, import, context, node);
+// case BuiltinFnIdSetFnVisible:
+// return analyze_set_fn_visible(g, import, context, node);
+// case BuiltinFnIdSetDebugSafety:
+// return analyze_set_debug_safety(g, import, context, node);
+// }
+// zig_unreachable();
+//}
+
+
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@@ -1533,6 +2949,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
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 IrInstructionIdLoadVar:
@@ -1540,6 +2958,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
case IrInstructionIdCall:
return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
case IrInstructionIdCondBr:
+ case IrInstructionIdBr:
case IrInstructionIdSwitchBr:
case IrInstructionIdPhi:
case IrInstructionIdStoreVar:
@@ -1582,9 +3001,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
TypeTableEntry *return_type = ira->codegen->builtin_types.entry_void;
- ira->new_irb.current_basic_block = allocate<IrBasicBlock>(1);
- ira->new_irb.exec->basic_block_list.append(ira->new_irb.current_basic_block);
-
+ ira->new_irb.current_basic_block = ir_build_basic_block(&ira->new_irb, "Entry");
ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(0);
ira->new_irb.current_basic_block->other = ira->old_irb.current_basic_block;
src/ir_print.cpp
@@ -46,10 +46,17 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction)
case TypeTableEntryIdMetaType:
fprintf(irp->f, "%s\n", buf_ptr(&const_instruction->base.static_value.data.x_type->name));
break;
+ case TypeTableEntryIdInt:
+ {
+ BigNum *bignum = &const_instruction->base.static_value.data.x_bignum;
+ assert(bignum->kind == BigNumKindInt);
+ const char *negative_str = bignum->is_negative ? "-" : "";
+ fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint);
+ }
+ break;
case TypeTableEntryIdVar:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
- case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
@@ -127,6 +134,47 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
zig_unreachable();
}
+static const char *ir_un_op_id_str(IrUnOp op_id) {
+ switch (op_id) {
+ case IrUnOpInvalid:
+ zig_unreachable();
+ case IrUnOpBoolNot:
+ return "!";
+ case IrUnOpBinNot:
+ return "~";
+ case IrUnOpNegation:
+ return "-";
+ case IrUnOpNegationWrap:
+ return "-%";
+ case IrUnOpAddressOf:
+ return "&";
+ case IrUnOpConstAddressOf:
+ return "&const";
+ case IrUnOpDereference:
+ return "*";
+ case IrUnOpMaybe:
+ return "?";
+ case IrUnOpError:
+ return "%";
+ case IrUnOpUnwrapError:
+ return "%%";
+ case IrUnOpUnwrapMaybe:
+ return "??";
+ case IrUnOpMaybeReturn:
+ return "?return";
+ case IrUnOpErrorReturn:
+ return "%return";
+ }
+ zig_unreachable();
+}
+
+static void ir_print_un_op(IrPrint *irp, IrInstructionUnOp *un_op_instruction) {
+ ir_print_prefix(irp, &un_op_instruction->base);
+ fprintf(irp->f, "%s #%zu\n",
+ ir_un_op_id_str(un_op_instruction->op_id),
+ un_op_instruction->value->debug_id);
+}
+
static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction) {
ir_print_prefix(irp, &bin_op_instruction->base);
fprintf(irp->f, "#%zu %s #%zu\n",
@@ -160,6 +208,47 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
fprintf(irp->f, ")\n");
}
+static void ir_print_builtin_call(IrPrint *irp, IrInstructionBuiltinCall *call_instruction) {
+ ir_print_prefix(irp, &call_instruction->base);
+ 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, ", ");
+ fprintf(irp->f, "#%zu", arg->debug_id);
+ }
+ fprintf(irp->f, ")\n");
+}
+
+
+static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) {
+ ir_print_prefix(irp, &cond_br_instruction->base);
+ fprintf(irp->f, "if #%zu then $%s_%zu else $%s_%zu\n",
+ cond_br_instruction->condition->debug_id,
+ cond_br_instruction->then_block->name_hint, cond_br_instruction->then_block->debug_id,
+ cond_br_instruction->else_block->name_hint, cond_br_instruction->else_block->debug_id);
+}
+
+static void ir_print_br(IrPrint *irp, IrInstructionBr *br_instruction) {
+ ir_print_prefix(irp, &br_instruction->base);
+ fprintf(irp->f, "goto $%s_%zu\n",
+ br_instruction->dest_block->name_hint, br_instruction->dest_block->debug_id);
+}
+
+static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) {
+ ir_print_prefix(irp, &phi_instruction->base);
+ for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
+ IrBasicBlock *incoming_block = phi_instruction->incoming_blocks[i];
+ IrInstruction *incoming_value = phi_instruction->incoming_values[i];
+ if (i != 0)
+ fprintf(irp->f, " ");
+ fprintf(irp->f, "$%s_%zu:#%zu",
+ incoming_block->name_hint, incoming_block->debug_id,
+ incoming_value->debug_id);
+ }
+ fprintf(irp->f, "\n");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@@ -182,11 +271,23 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdCall:
ir_print_call(irp, (IrInstructionCall *)instruction);
break;
+ case IrInstructionIdUnOp:
+ ir_print_un_op(irp, (IrInstructionUnOp *)instruction);
+ break;
case IrInstructionIdCondBr:
- case IrInstructionIdSwitchBr:
+ ir_print_cond_br(irp, (IrInstructionCondBr *)instruction);
+ break;
+ 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;
+ case IrInstructionIdSwitchBr:
case IrInstructionIdStoreVar:
- case IrInstructionIdBuiltinCall:
zig_panic("TODO print more IR instructions");
}
}
@@ -200,6 +301,7 @@ void ir_print(FILE *f, IrExecutable *executable, int indent_size) {
for (size_t bb_i = 0; bb_i < executable->basic_block_list.length; bb_i += 1) {
IrBasicBlock *current_block = executable->basic_block_list.at(bb_i);
+ fprintf(irp->f, "%s_%zu:\n", current_block->name_hint, current_block->debug_id);
for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) {
IrInstruction *instruction = current_block->instruction_list.at(instr_i);
ir_print_instruction(irp, instruction);