Commit 71d95c6597
Changed files (5)
src/all_types.hpp
@@ -1453,6 +1453,8 @@ enum IrInstructionId {
IrInstructionIdAsm,
IrInstructionIdCompileVar,
IrInstructionIdSizeOf,
+ IrInstructionIdTestNull,
+ IrInstructionIdUnwrapMaybe,
};
struct IrInstruction {
@@ -1749,6 +1751,21 @@ struct IrInstructionSizeOf {
IrInstruction *type_value;
};
+// returns true if nonnull, returns false if null
+// this is so that `zeroes` sets maybe values to null
+struct IrInstructionTestNull {
+ IrInstruction base;
+
+ IrInstruction *value;
+};
+
+struct IrInstructionUnwrapMaybe {
+ IrInstruction base;
+
+ IrInstruction *value;
+ bool safety_check_on;
+};
+
enum LValPurpose {
LValPurposeNone,
LValPurposeAssign,
@@ -1758,4 +1775,7 @@ enum LValPurpose {
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
+static const size_t maybe_child_index = 0;
+static const size_t maybe_null_index = 1;
+
#endif
src/ast_render.cpp
@@ -706,6 +706,27 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, "null");
break;
}
+ case NodeTypeIfVarExpr:
+ {
+ AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl;
+ const char *var_str = var_decl->is_const ? "const" : "var";
+ const char *var_name = buf_ptr(var_decl->symbol);
+ const char *ptr_str = node->data.if_var_expr.var_is_ptr ? "*" : "";
+ fprintf(ar->f, "if (%s %s%s", var_str, ptr_str, var_name);
+ if (var_decl->type) {
+ fprintf(ar->f, ": ");
+ render_node_ungrouped(ar, var_decl->type);
+ }
+ fprintf(ar->f, " = ");
+ render_node_grouped(ar, var_decl->expr);
+ fprintf(ar->f, ") ");
+ render_node_grouped(ar, node->data.if_var_expr.then_block);
+ if (node->data.if_var_expr.else_node) {
+ fprintf(ar->f, " else ");
+ render_node_grouped(ar, node->data.if_var_expr.else_node);
+ }
+ break;
+ }
case NodeTypeFnDecl:
case NodeTypeParamDecl:
case NodeTypeErrorValueDecl:
@@ -715,7 +736,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeStructValueField:
case NodeTypeUse:
case NodeTypeZeroesLiteral:
- case NodeTypeIfVarExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
src/codegen.cpp
@@ -1427,7 +1427,56 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
}
+// 0 - null, 1 - non null
+static LLVMValueRef gen_null_bit(CodeGen *g, TypeTableEntry *ptr_type, LLVMValueRef maybe_ptr) {
+ assert(ptr_type->id == TypeTableEntryIdPointer);
+ TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type;
+ assert(maybe_type->id == TypeTableEntryIdMaybe);
+ TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
+ LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type);
+ bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn);
+ if (maybe_is_ptr) {
+ return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref, LLVMConstNull(child_type->type_ref), "");
+ } else {
+ LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, maybe_null_index, "");
+ return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+ }
+}
+
+static LLVMValueRef ir_render_test_null(CodeGen *g, IrExecutable *executable, IrInstructionTestNull *instruction) {
+ TypeTableEntry *ptr_type = instruction->value->type_entry;
+ assert(ptr_type->id == TypeTableEntryIdPointer);
+ return gen_null_bit(g, ptr_type, ir_llvm_value(g, instruction->value));
+}
+
+static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
+ IrInstructionUnwrapMaybe *instruction)
+{
+ TypeTableEntry *ptr_type = instruction->value->type_entry;
+ assert(ptr_type->id == TypeTableEntryIdPointer);
+ TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type;
+ assert(maybe_type->id == TypeTableEntryIdMaybe);
+ TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
+ bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn);
+ LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value);
+ if (ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on) {
+ LLVMValueRef nonnull_bit = gen_null_bit(g, ptr_type, maybe_ptr);
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeOk");
+ LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeFail");
+ LLVMBuildCondBr(g->builder, nonnull_bit, ok_block, fail_block);
+ LLVMPositionBuilderAtEnd(g->builder, fail_block);
+ gen_debug_safety_crash(g);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ }
+ if (maybe_is_ptr) {
+ return maybe_ptr;
+ } else {
+ LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type);
+ return LLVMBuildStructGEP(g->builder, maybe_struct_ref, maybe_child_index, "");
+ }
+}
static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
set_debug_source_node(g, instruction->source_node);
@@ -1476,6 +1525,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
case IrInstructionIdAsm:
return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
+ case IrInstructionIdTestNull:
+ return ir_render_test_null(g, executable, (IrInstructionTestNull *)instruction);
+ case IrInstructionIdUnwrapMaybe:
+ return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction);
case IrInstructionIdSwitchBr:
case IrInstructionIdPhi:
case IrInstructionIdContainerInitList:
src/ir.cpp
@@ -226,6 +226,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) {
return IrInstructionIdSizeOf;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNull *) {
+ return IrInstructionIdTestNull;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) {
+ return IrInstructionIdUnwrapMaybe;
+}
+
template<typename T>
static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@@ -878,6 +886,44 @@ static IrInstruction *ir_build_size_of(IrBuilder *irb, AstNode *source_node, IrI
return &instruction->base;
}
+static IrInstruction *ir_build_test_null(IrBuilder *irb, AstNode *source_node, IrInstruction *value) {
+ IrInstructionTestNull *instruction = ir_build_instruction<IrInstructionTestNull>(irb, source_node);
+ instruction->value = value;
+
+ ir_ref_instruction(value);
+
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_test_null_from(IrBuilder *irb, IrInstruction *old_instruction,
+ IrInstruction *value)
+{
+ IrInstruction *new_instruction = ir_build_test_null(irb, old_instruction->source_node, value);
+ ir_link_new_instruction(new_instruction, old_instruction);
+ return new_instruction;
+}
+
+static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, AstNode *source_node, IrInstruction *value,
+ bool safety_check_on)
+{
+ IrInstructionUnwrapMaybe *instruction = ir_build_instruction<IrInstructionUnwrapMaybe>(irb, source_node);
+ instruction->value = value;
+ instruction->safety_check_on = safety_check_on;
+
+ ir_ref_instruction(value);
+
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *old_instruction,
+ IrInstruction *value, bool safety_check_on)
+{
+ IrInstruction *new_instruction = ir_build_unwrap_maybe(irb, old_instruction->source_node,
+ value, safety_check_on);
+ ir_link_new_instruction(new_instruction, old_instruction);
+ return new_instruction;
+}
+
static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
bool gen_error_defers, bool gen_maybe_defers)
{
@@ -1164,7 +1210,6 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, AstNode *node) {
return ir_build_const_null(irb, node);
}
-
static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstNode *decl_node,
LValPurpose lval, BlockContext *scope)
{
@@ -1502,6 +1547,15 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, AstNode *node, IrUnOp
return ir_gen_prefix_op_id_lval(irb, node, op_id, LValPurposeNone);
}
+static IrInstruction *ir_gen_prefix_op_unwrap_maybe(IrBuilder *irb, AstNode *node) {
+ AstNode *expr = node->data.prefix_op_expr.primary_expr;
+ IrInstruction *value = ir_gen_node(irb, expr, node->block_context);
+ if (value == irb->codegen->invalid_instruction)
+ return value;
+
+ return ir_build_unwrap_maybe(irb, node, value, true);
+}
+
static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node, LValPurpose lval) {
assert(node->type == NodeTypePrefixOpExpr);
@@ -1531,7 +1585,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node, LValP
case PrefixOpUnwrapError:
return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapError);
case PrefixOpUnwrapMaybe:
- return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapMaybe);
+ return ir_gen_prefix_op_unwrap_maybe(irb, node);
}
zig_unreachable();
}
@@ -1883,6 +1937,69 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, AstNode *node) {
return ir_build_asm(irb, node, input_list, output_types, return_count, is_volatile);
}
+static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, AstNode *node) {
+ assert(node->type == NodeTypeIfVarExpr);
+
+ AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl;
+ AstNode *expr_node = var_decl->expr;
+ AstNode *then_node = node->data.if_var_expr.then_block;
+ AstNode *else_node = node->data.if_var_expr.else_node;
+ bool var_is_ptr = node->data.if_var_expr.var_is_ptr;
+
+ IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, node->block_context, LValPurposeAddressOf);
+ if (expr_value == irb->codegen->invalid_instruction)
+ return expr_value;
+
+ IrInstruction *is_nonnull_value = ir_build_test_null(irb, node, expr_value);
+
+ IrBasicBlock *then_block = ir_build_basic_block(irb, "MaybeThen");
+ IrBasicBlock *else_block = ir_build_basic_block(irb, "MaybeElse");
+ IrBasicBlock *endif_block = ir_build_basic_block(irb, "MaybeEndIf");
+
+ bool is_inline = (node->block_context->fn_entry == nullptr);
+ ir_build_cond_br(irb, node, is_nonnull_value, then_block, else_block, is_inline);
+
+ ir_set_cursor_at_end(irb, then_block);
+ IrInstruction *var_type = nullptr;
+ if (var_decl->type)
+ var_type = ir_gen_node(irb, var_decl->type, node->block_context);
+ BlockContext *child_scope = new_block_context(node, node->block_context);
+ bool is_shadowable = false;
+ bool is_const = var_decl->is_const;
+ VariableTableEntry *var = ir_add_local_var(irb, node, child_scope,
+ var_decl->symbol, is_const, is_const, is_shadowable, is_inline);
+ IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, node, expr_value, false);
+ IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, node, var_ptr_value);
+ ir_build_var_decl(irb, node, var, var_type, var_value);
+ IrInstruction *then_expr_result = ir_gen_node(irb, then_node, child_scope);
+ 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, is_inline);
+
+ 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, is_inline);
+
+ 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_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
LValPurpose lval)
{
@@ -1932,10 +2049,11 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
return ir_gen_asm_expr(irb, node);
case NodeTypeNullLiteral:
return ir_gen_null_literal(irb, node);
+ case NodeTypeIfVarExpr:
+ return ir_gen_if_var_expr(irb, node);
case NodeTypeUnwrapErrorExpr:
case NodeTypeDefer:
case NodeTypeSliceExpr:
- case NodeTypeIfVarExpr:
case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
@@ -4488,6 +4606,82 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
zig_unreachable();
}
+static TypeTableEntry *ir_analyze_instruction_test_null(IrAnalyze *ira,
+ IrInstructionTestNull *test_null_instruction)
+{
+ IrInstruction *value = test_null_instruction->value->other;
+ if (value->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ // This will be a pointer type because test null IR instruction operates on a pointer to a thing.
+ TypeTableEntry *ptr_type = value->type_entry;
+ assert(ptr_type->id == TypeTableEntryIdPointer);
+
+ TypeTableEntry *type_entry = ptr_type->data.pointer.child_type;
+ if (type_entry->id != TypeTableEntryIdMaybe) {
+ add_node_error(ira->codegen, test_null_instruction->base.source_node,
+ buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (value->static_value.special != ConstValSpecialRuntime) {
+ ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr;
+ assert(value->static_value.data.x_ptr.index == SIZE_MAX);
+
+ if (maybe_val->special != ConstValSpecialRuntime) {
+ bool depends_on_compile_var = maybe_val->depends_on_compile_var;
+ ConstExprValue *out_val = ir_build_const_from(ira, &test_null_instruction->base,
+ depends_on_compile_var);
+ out_val->data.x_bool = (maybe_val->data.x_maybe == nullptr);
+ return ira->codegen->builtin_types.entry_bool;
+ }
+ }
+
+ ir_build_test_null_from(&ira->new_irb, &test_null_instruction->base, value);
+ return ira->codegen->builtin_types.entry_bool;
+}
+
+static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
+ IrInstructionUnwrapMaybe *unwrap_maybe_instruction)
+{
+ IrInstruction *value = unwrap_maybe_instruction->value->other;
+ if (value->type_entry->id == TypeTableEntryIdInvalid)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ // This will be a pointer type because test null IR instruction operates on a pointer to a thing.
+ TypeTableEntry *ptr_type = value->type_entry;
+ assert(ptr_type->id == TypeTableEntryIdPointer);
+
+ TypeTableEntry *type_entry = ptr_type->data.pointer.child_type;
+ if (type_entry->id == TypeTableEntryIdInvalid) {
+ return ira->codegen->builtin_types.entry_invalid;
+ } else if (type_entry->id != TypeTableEntryIdMaybe) {
+ add_node_error(ira->codegen, unwrap_maybe_instruction->base.source_node,
+ buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ TypeTableEntry *child_type = type_entry->data.maybe.child_type;
+ TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, child_type, false);
+
+ if (value->static_value.special != ConstValSpecialRuntime) {
+ ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr;
+ assert(value->static_value.data.x_ptr.index == SIZE_MAX);
+
+ if (maybe_val->special != ConstValSpecialRuntime) {
+ bool depends_on_compile_var = maybe_val->depends_on_compile_var;
+ ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base,
+ depends_on_compile_var);
+ out_val->data.x_ptr.base_ptr = maybe_val;
+ out_val->data.x_ptr.index = SIZE_MAX;
+ return result_type;
+ }
+ }
+
+ ir_build_unwrap_maybe_from(&ira->new_irb, &unwrap_maybe_instruction->base, value,
+ unwrap_maybe_instruction->safety_check_on);
+ return result_type;
+}
+
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@@ -4544,6 +4738,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_compile_var(ira, (IrInstructionCompileVar *)instruction);
case IrInstructionIdSizeOf:
return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
+ case IrInstructionIdTestNull:
+ return ir_analyze_instruction_test_null(ira, (IrInstructionTestNull *)instruction);
+ case IrInstructionIdUnwrapMaybe:
+ return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction);
case IrInstructionIdSwitchBr:
case IrInstructionIdCast:
case IrInstructionIdContainerInitList:
@@ -4654,6 +4852,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdSliceType:
case IrInstructionIdCompileVar:
case IrInstructionIdSizeOf:
+ case IrInstructionIdTestNull:
+ case IrInstructionIdUnwrapMaybe:
return false;
case IrInstructionIdAsm:
{
@@ -6266,32 +6466,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return result_type;
//}
//
-//static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
-// TypeTableEntry *expected_type, AstNode *node)
-//{
-// assert(node->type == NodeTypeIfVarExpr);
-//
-// BlockContext *child_context = new_block_context(node, parent_context);
-//
-// analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true,
-// nullptr, node->data.if_var_expr.var_is_ptr);
-// VariableTableEntry *var = node->data.if_var_expr.var_decl.variable;
-// if (var->type->id == TypeTableEntryIdInvalid) {
-// return g->builtin_types.entry_invalid;
-// }
-// AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr;
-// ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val;
-// bool cond_is_const = var_const_val->ok;
-// bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false;
-//
-//
-// AstNode **then_node = &node->data.if_var_expr.then_block;
-// AstNode **else_node = &node->data.if_var_expr.else_node;
-//
-// return analyze_if(g, import, child_context, expected_type,
-// node, then_node, else_node, cond_is_const, cond_bool_val);
-//}
-//
//static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type,
// TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry)
//{
@@ -7407,92 +7581,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// }
//}
//
-//
-//static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
-// BlockContext *context, AstNode *source_node,
-// AstNodeVariableDeclaration *variable_declaration,
-// bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr)
-//{
-// bool is_const = variable_declaration->is_const;
-// bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport);
-// bool is_extern = variable_declaration->is_extern;
-//
-// TypeTableEntry *explicit_type = nullptr;
-// if (variable_declaration->type != nullptr) {
-// explicit_type = analyze_type_expr(g, import, context, variable_declaration->type);
-// if (explicit_type->id == TypeTableEntryIdUnreachable) {
-// add_node_error(g, variable_declaration->type,
-// buf_sprintf("variable of type 'unreachable' not allowed"));
-// explicit_type = g->builtin_types.entry_invalid;
-// }
-// }
-//
-// TypeTableEntry *implicit_type = nullptr;
-// if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) {
-// implicit_type = explicit_type;
-// } else if (variable_declaration->expr) {
-// implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
-// if (implicit_type->id == TypeTableEntryIdInvalid) {
-// // ignore the poison value
-// } else if (expr_is_maybe) {
-// if (implicit_type->id == TypeTableEntryIdMaybe) {
-// if (var_is_ptr) {
-// // TODO if the expression is constant, can't get pointer to it
-// implicit_type = get_pointer_to_type(g, implicit_type->data.maybe.child_type, false);
-// } else {
-// implicit_type = implicit_type->data.maybe.child_type;
-// }
-// } else {
-// add_node_error(g, variable_declaration->expr, buf_sprintf("expected maybe type"));
-// implicit_type = g->builtin_types.entry_invalid;
-// }
-// } else if (implicit_type->id == TypeTableEntryIdUnreachable) {
-// add_node_error(g, source_node,
-// buf_sprintf("variable initialization is unreachable"));
-// implicit_type = g->builtin_types.entry_invalid;
-// } else if ((!is_const || is_export) &&
-// (implicit_type->id == TypeTableEntryIdNumLitFloat ||
-// implicit_type->id == TypeTableEntryIdNumLitInt))
-// {
-// add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
-// implicit_type = g->builtin_types.entry_invalid;
-// } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
-// add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
-// implicit_type = g->builtin_types.entry_invalid;
-// }
-// if (implicit_type->id != TypeTableEntryIdInvalid && !context->fn_entry) {
-// ConstExprValue *const_val = &get_resolved_expr(variable_declaration->expr)->const_val;
-// if (!const_val->ok) {
-// add_node_error(g, first_executing_node(variable_declaration->expr),
-// buf_sprintf("global variable initializer requires constant expression"));
-// }
-// }
-// } else if (!is_extern) {
-// add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
-// implicit_type = g->builtin_types.entry_invalid;
-// }
-//
-// TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
-// assert(type != nullptr); // should have been caught by the parser
-//
-// VariableTableEntry *var = add_local_var(g, source_node, import, context,
-// variable_declaration->symbol, type, is_const,
-// expr_is_maybe ? nullptr : variable_declaration->expr);
-//
-// variable_declaration->variable = var;
-//
-// return var;
-//}
-//
-//static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
-// BlockContext *context, TypeTableEntry *expected_type, AstNode *node)
-//{
-// AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
-// return analyze_variable_declaration_raw(g, import, context, node, variable_declaration,
-// false, nullptr, false);
-//}
-//
-//
//static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
// TypeTableEntry *expected_type, AstNode *node)
//{
@@ -8581,64 +8669,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
// return nullptr;
//}
//
-//static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
-// assert(node->type == NodeTypeBinOpExpr);
-// assert(node->data.bin_op_expr.bin_op == BinOpTypeUnwrapMaybe);
-//
-// AstNode *op1_node = node->data.bin_op_expr.op1;
-// AstNode *op2_node = node->data.bin_op_expr.op2;
-//
-// LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node);
-//
-// TypeTableEntry *maybe_type = get_expr_type(op1_node);
-// assert(maybe_type->id == TypeTableEntryIdMaybe);
-// TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
-//
-// LLVMValueRef cond_value;
-// if (child_type->id == TypeTableEntryIdPointer ||
-// child_type->id == TypeTableEntryIdFn)
-// {
-// cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref,
-// LLVMConstNull(child_type->type_ref), "");
-// } else {
-// LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
-// cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
-// }
-//
-// LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull");
-// LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull");
-// LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd");
-//
-// bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable;
-//
-// LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block);
-//
-// LLVMPositionBuilderAtEnd(g->builder, non_null_block);
-// LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref);
-// LLVMBuildBr(g->builder, end_block);
-// LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder);
-//
-// LLVMPositionBuilderAtEnd(g->builder, null_block);
-// LLVMValueRef null_result = gen_expr(g, op2_node);
-// if (null_reachable) {
-// LLVMBuildBr(g->builder, end_block);
-// }
-// LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder);
-//
-// LLVMPositionBuilderAtEnd(g->builder, end_block);
-// if (null_reachable) {
-// LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
-// LLVMValueRef incoming_values[2] = {non_null_result, null_result};
-// LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block};
-// LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-// return phi;
-// } else {
-// return non_null_result;
-// }
-//
-// return nullptr;
-//}
-//
//static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
// assert(node->type == NodeTypeUnwrapErrorExpr);
//
@@ -8914,120 +8944,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
// }
//}
//
-//static LLVMValueRef gen_if_var_then_block(CodeGen *g, AstNode *node, VariableTableEntry *variable, bool maybe_is_ptr,
-// LLVMValueRef init_val, TypeTableEntry *child_type, AstNode *then_node)
-//{
-// if (node->data.if_var_expr.var_is_ptr) {
-// LLVMValueRef payload_ptr;
-// if (maybe_is_ptr) {
-// zig_panic("TODO");
-// } else {
-// payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, "");
-// }
-// LLVMBuildStore(g->builder, payload_ptr, variable->value_ref);
-// } else {
-// LLVMValueRef payload_val;
-// if (maybe_is_ptr) {
-// payload_val = init_val;
-// } else {
-// LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, "");
-// payload_val = get_handle_value(g, payload_ptr, child_type);
-// }
-// gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val,
-// variable->type, child_type);
-// }
-// gen_var_debug_decl(g, variable);
-//
-// return gen_expr(g, then_node);
-//}
-//
-//static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
-// assert(node->type == NodeTypeIfVarExpr);
-// assert(node->data.if_var_expr.var_decl.expr);
-//
-// AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl;
-// VariableTableEntry *variable = var_decl->variable;
-//
-// // test if value is the maybe state
-// TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
-// TypeTableEntry *child_type = expr_type->data.maybe.child_type;
-//
-// LLVMValueRef init_val = gen_expr(g, var_decl->expr);
-//
-//
-// AstNode *then_node = node->data.if_var_expr.then_block;
-// AstNode *else_node = node->data.if_var_expr.else_node;
-// bool maybe_is_ptr = child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
-//
-// ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
-// if (const_val->ok) {
-// if (const_val->data.x_maybe) {
-// return gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node);
-// } else {
-// return gen_expr(g, else_node);
-// }
-// }
-//
-// LLVMValueRef cond_value;
-// if (maybe_is_ptr) {
-// cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), "");
-// } else {
-// LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, "");
-// cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
-// }
-//
-// TypeTableEntry *then_type = get_expr_type(then_node);
-// TypeTableEntry *else_type = get_expr_type(else_node);
-//
-// bool use_then_value = type_has_bits(then_type);
-// bool use_else_value = type_has_bits(else_type);
-//
-// LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeThen");
-// LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeElse");
-//
-// LLVMBasicBlockRef endif_block;
-// bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
-// bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
-// if (then_endif_reachable || else_endif_reachable) {
-// endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEndIf");
-// }
-//
-// LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
-//
-// LLVMPositionBuilderAtEnd(g->builder, then_block);
-// LLVMValueRef then_expr_result = gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node);
-//
-// if (then_endif_reachable) {
-// LLVMBuildBr(g->builder, endif_block);
-// }
-// LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
-//
-//
-// LLVMPositionBuilderAtEnd(g->builder, else_block);
-// LLVMValueRef else_expr_result = gen_expr(g, else_node);
-// if (else_endif_reachable) {
-// LLVMBuildBr(g->builder, endif_block);
-// }
-// LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
-//
-// if (then_endif_reachable || else_endif_reachable) {
-// LLVMPositionBuilderAtEnd(g->builder, endif_block);
-// if (use_then_value && use_else_value) {
-// LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
-// LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
-// LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
-// LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-// return phi;
-// } else if (use_then_value) {
-// return then_expr_result;
-// } else if (use_else_value) {
-// return else_expr_result;
-// }
-// }
-//
-// return nullptr;
-//}
-//
//static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
// assert(block_node->type == NodeTypeBlock);
//
@@ -9667,20 +9583,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
// zig_unreachable();
//}
//
-//static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) {
-// TypeTableEntry *type_entry = get_expr_type(node);
-// assert(type_entry->id == TypeTableEntryIdMaybe);
-// TypeTableEntry *child_type = type_entry->data.maybe.child_type;
-// if (child_type->id == TypeTableEntryIdPointer ||
-// child_type->id == TypeTableEntryIdFn)
-// {
-// return maybe_struct_ref;
-// } else {
-// LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
-// return get_handle_value(g, maybe_field_ptr, child_type);
-// }
-//}
-//
//static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
// size_t result = 0;
// while (inner_block != outer_block) {
src/ir_print.cpp
@@ -497,6 +497,20 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) {
fprintf(irp->f, ")");
}
+static void ir_print_test_null(IrPrint *irp, IrInstructionTestNull *instruction) {
+ fprintf(irp->f, "*");
+ ir_print_other_instruction(irp, instruction->value);
+ fprintf(irp->f, " == null");
+}
+
+static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) {
+ fprintf(irp->f, "&??*");
+ ir_print_other_instruction(irp, instruction->value);
+ if (!instruction->safety_check_on) {
+ fprintf(irp->f, " // no safety");
+ }
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@@ -592,6 +606,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdSizeOf:
ir_print_size_of(irp, (IrInstructionSizeOf *)instruction);
break;
+ case IrInstructionIdTestNull:
+ ir_print_test_null(irp, (IrInstructionTestNull *)instruction);
+ break;
+ case IrInstructionIdUnwrapMaybe:
+ ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction);
+ break;
case IrInstructionIdSwitchBr:
zig_panic("TODO print more IR instructions");
}