Zig code:
const x = blk: {
const y: i32 = foo();
break :blk y;
};
current implementation (on my PC) is reusing the alloc inferred mechanism to determine the type of x
. see the ZIR code:
%9 = alloc_inferred()
%10 = block({
%11 = dbg_stmt()
%12 = const(TypedValue{ .ty = type, .val = type})
%13 = const(TypedValue{ .ty = type, .val = i32})
%14 = as(%12, %13)
%15 = alloc(%14)
%16 = declval_in_module(Decl(foo))
%17 = deref(%16)
%18 = call(%17, [], modifier=auto)
%19 = coerce_to_ptr_elem(%15, %18)
%20 = store(%15, %19)
%21 = dbg_stmt()
%22 = store_to_inferred_ptr(%9, %19)
%23 = break("label_10", %19)
})
%24 = resolve_inferred_alloc(%9)
This also works for a more complicate example:
const x = if (bar()) blk: {
const y = foo();
break :blk y;
} else blk: {
var z = baz();
break :blk z;
};
ZIR:
%9 = alloc_inferred()
%10 = block({
%11 = const(TypedValue{ .ty = type, .val = bool})
%12 = declval_in_module(Decl(bar))
%13 = deref(%12)
%14 = call(%13, [], modifier=auto)
%15 = as(%11, %14)
%16 = condbr(%15, {
%17 = block({
%18 = dbg_stmt()
%19 = alloc_inferred()
%20 = declval_in_module(Decl(foo))
%21 = deref(%20)
%22 = call(%21, [], modifier=auto)
%23 = store_to_inferred_ptr(%19, %22)
%24 = resolve_inferred_alloc(%19)
%25 = dbg_stmt()
%26 = store_to_inferred_ptr(%9, %22)
%27 = break("label_17", %22)
})
%28 = break("label_10", %17)
}, {
%29 = block({
%30 = dbg_stmt()
%31 = alloc_inferred_mut()
%32 = declval_in_module(Decl(baz))
%33 = deref(%32)
%34 = call(%33, [], modifier=auto)
%35 = store_to_inferred_ptr(%31, %34)
%36 = resolve_inferred_alloc(%31)
%37 = dbg_stmt()
%38 = deref(%31)
%39 = store_to_inferred_ptr(%9, %38)
%40 = break("label_29", %38)
})
%41 = break("label_10", %29)
})
})
%42 = resolve_inferred_alloc(%9)
Zig code:
var x: i64 = 1;
var y: i32 = 2;
var thing: bool = true;
const result = if(thing) x else y;
ZIR:
%25 = alloc_inferred()
%26 = block({
%27 = const(TypedValue{ .ty = type, .val = bool})
%28 = deref(%20)
%29 = as(%27, %28)
%30 = condbr(%29, {
%31 = deref(%4)
%32 = store_to_inferred_ptr(%25, %31)
%33 = break("label_26", %31)
}, {
%34 = deref(%12)
%35 = store_to_inferred_ptr(%25, %34)
%36 = break("label_26", %34)
})
})
%37 = resolve_inferred_alloc(%25)
The problem is in the TZIR below. As you can see the block instruction (%11
) gets the type resolved through the values of the break instructions that point to the block. In this case there is a i32
and i64
which gets resolved to a i64
. This is correct, however the break for the i32
(%21
) still breaks an i32
value to the block, which is not correct. That should be a i64
. I did manage to patch up the TZIR after the are the instructions in the block are generated, but that felt kinda like a hack.
TZIR:
%10: *i64 = alloc()
%11: i64 = block(
%12: bool = load(%7)
%13: noreturn = condbr(%12,
then:
%14: i64 = load(%1)
%15: *i64 = bitcast(%10)
%16: void = store(%15, %14)
%17: noreturn = br(%11, %14)
else:
%18: i32 = load(%4)
%19: *i32 = bitcast(%10)
%20: void = store(%19, %18)
%21: noreturn = br(%11, %18)
)
)
diff --git a/src/astgen.zig b/src/astgen.zig
index 8b4f1cc93..74a55f390 100644
--- a/src/astgen.zig
+++ b/src/astgen.zig
@@ -372,8 +372,8 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr
// proper type inference requires peer type resolution on the block's
// break operand expressions.
const branch_rl: ResultLoc = switch (gen_zir.break_result_loc) {
- .discard, .none, .ty, .ptr, .ref => gen_zir.break_result_loc,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block_inst },
+ .discard, .none, .ty, .ptr, .ref, .inferred_ptr => gen_zir.break_result_loc,
+ .bitcasted_ptr, .block_ptr => .{ .block_ptr = block_inst },
};
const operand = try expr(mod, parent_scope, branch_rl, rhs);
return try addZIRInst(mod, parent_scope, src, zir.Inst.Break, .{
@@ -1617,8 +1617,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
// proper type inference requires peer type resolution on the if's
// branches.
const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
+ .discard, .none, .ty, .ptr, .ref, .inferred_ptr => rl,
+ .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
};
const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body);
@@ -1763,8 +1763,8 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
// proper type inference requires peer type resolution on the while's
// branches.
const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
+ .discard, .none, .ty, .ptr, .ref, .inferred_ptr => rl,
+ .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
};
const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body);
@@ -1927,8 +1927,8 @@ fn forExpr(
// proper type inference requires peer type resolution on the while's
// branches.
const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
+ .discard, .none, .ty, .ptr, .ref, .inferred_ptr => rl,
+ .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
};
var index_scope: Scope.LocalPtr = undefined;
@@ -2055,8 +2055,8 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
// if we need to write to a pointer which has an inferred type,
// proper type inference requires peer type resolution on the switch case.
const case_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
+ .discard, .none, .ty, .ptr, .ref, .inferred_ptr => rl,
+ .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
};
var item_scope: Scope.GenZIR = .{