Skip to content

Instantly share code, notes, and snippets.

@FireFox317
Last active January 19, 2021 23:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FireFox317/21a0109030491b3a81fe579b0052fe40 to your computer and use it in GitHub Desktop.
Save FireFox317/21a0109030491b3a81fe579b0052fe40 to your computer and use it in GitHub Desktop.
inferred blocks

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)

Another edge case

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

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 = .{
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment