Commit 31023de6c4
Changed files (6)
lib/std/zig/parse.zig
@@ -59,10 +59,7 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
parser.nodes.appendAssumeCapacity(.{
.tag = .root,
.main_token = 0,
- .data = .{
- .lhs = undefined,
- .rhs = undefined,
- },
+ .data = undefined,
});
const root_members = try parser.parseContainerMembers();
const root_decls = try root_members.toSpan(&parser);
src/astgen.zig
@@ -699,7 +699,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro
};
if (rhs == 0) {
- _ = try parent_gz.addBreakVoid(block_inst, node);
+ _ = try parent_gz.addBreak(.@"break", block_inst, .void_value);
return zir.Inst.Ref.unreachable_value;
}
block_gz.break_count += 1;
@@ -707,7 +707,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro
const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs);
const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;
- const br = try parent_gz.addBreak(block_inst, operand);
+ const br = try parent_gz.addBreak(.@"break", block_inst, operand);
if (block_gz.break_result_loc == .block_ptr) {
try block_gz.labeled_breaks.append(mod.gpa, br);
@@ -860,7 +860,7 @@ fn labeledBlockExpr(
const tracy = trace(@src());
defer tracy.end();
- assert(zir_tag == .block or zir_tag == .block_comptime);
+ assert(zir_tag == .block);
const tree = parent_scope.tree();
const main_tokens = tree.nodes.items(.main_token);
@@ -911,8 +911,6 @@ fn labeledBlockExpr(
for (block_scope.labeled_breaks.items) |br| {
zir_datas[br].@"break".operand = .void_value;
}
- // TODO technically not needed since we changed the tag to break_void but
- // would be better still to elide the ones that are in this list.
try block_scope.setBlockBody(block_inst);
return gz.zir_code.indexToRef(block_inst);
@@ -1027,7 +1025,7 @@ fn blockExprStmts(
.bitcast_result_ptr,
.bit_or,
.block,
- .block_comptime,
+ .block_inline,
.loop,
.bool_br_and,
.bool_br_or,
@@ -1122,9 +1120,9 @@ fn blockExprStmts(
.compile_log,
.ensure_err_payload_void,
.@"break",
- .break_void_node,
- .break_flat,
+ .break_inline,
.condbr,
+ .condbr_inline,
.compile_error,
.ret_node,
.ret_tok,
@@ -1663,7 +1661,7 @@ fn orelseCatchExpr(
};
const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
const cond = try block_scope.addUnNode(cond_op, operand, node);
- const condbr = try block_scope.addCondBr(node);
+ const condbr = try block_scope.addCondBr(.condbr, node);
const block = try parent_gz.addBlock(.block, node);
try parent_gz.instructions.append(mod.gpa, block);
@@ -1731,6 +1729,7 @@ fn orelseCatchExpr(
else_result,
block,
block,
+ .@"break",
);
}
@@ -1750,6 +1749,7 @@ fn finishThenElseBlock(
else_result: zir.Inst.Ref,
main_block: zir.Inst.Index,
then_break_block: zir.Inst.Index,
+ break_tag: zir.Inst.Tag,
) InnerError!zir.Inst.Ref {
// We now have enough information to decide whether the result instruction should
// be communicated via result location pointer or break instructions.
@@ -1758,11 +1758,11 @@ fn finishThenElseBlock(
switch (strat.tag) {
.break_void => {
if (!wzc.refIsNoReturn(then_result)) {
- _ = try then_scope.addBreakVoid(then_break_block, then_src);
+ _ = try then_scope.addBreak(break_tag, then_break_block, .void_value);
}
const elide_else = if (else_result != .none) wzc.refIsNoReturn(else_result) else false;
if (!elide_else) {
- _ = try else_scope.addBreakVoid(main_block, else_src);
+ _ = try else_scope.addBreak(break_tag, main_block, .void_value);
}
assert(!strat.elide_store_to_block_ptr_instructions);
try setCondBrPayload(condbr, cond, then_scope, else_scope);
@@ -1770,14 +1770,14 @@ fn finishThenElseBlock(
},
.break_operand => {
if (!wzc.refIsNoReturn(then_result)) {
- _ = try then_scope.addBreak(then_break_block, then_result);
+ _ = try then_scope.addBreak(break_tag, then_break_block, then_result);
}
if (else_result != .none) {
if (!wzc.refIsNoReturn(else_result)) {
- _ = try else_scope.addBreak(main_block, else_result);
+ _ = try else_scope.addBreak(break_tag, main_block, else_result);
}
} else {
- _ = try else_scope.addBreakVoid(main_block, else_src);
+ _ = try else_scope.addBreak(break_tag, main_block, .void_value);
}
if (strat.elide_store_to_block_ptr_instructions) {
try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
@@ -1944,7 +1944,7 @@ fn boolBinOp(
};
defer rhs_scope.instructions.deinit(mod.gpa);
const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);
- _ = try rhs_scope.addUnNode(.break_flat, rhs, node);
+ _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
try rhs_scope.setBoolBrBody(bool_br);
const block_ref = gz.zir_code.indexToRef(bool_br);
@@ -1979,7 +1979,7 @@ fn ifExpr(
}
};
- const condbr = try block_scope.addCondBr(node);
+ const condbr = try block_scope.addCondBr(.condbr, node);
const block = try parent_gz.addBlock(.block, node);
try parent_gz.instructions.append(mod.gpa, block);
@@ -2042,6 +2042,7 @@ fn ifExpr(
else_info.result,
block,
block,
+ .@"break",
);
}
@@ -2108,7 +2109,9 @@ fn whileExpr(
try checkLabelRedefinition(mod, scope, label_token);
}
const parent_gz = scope.getGenZir();
- const loop_block = try parent_gz.addBlock(.loop, node);
+ const is_inline = while_full.inline_token != null;
+ const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
+ const loop_block = try parent_gz.addBlock(loop_tag, node);
try parent_gz.instructions.append(mod.gpa, loop_block);
var loop_scope: Scope.GenZir = .{
@@ -2140,8 +2143,10 @@ fn whileExpr(
}
};
- const condbr = try continue_scope.addCondBr(node);
- const cond_block = try loop_scope.addBlock(.block, node);
+ const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
+ const condbr = try continue_scope.addCondBr(condbr_tag, node);
+ const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
+ const cond_block = try loop_scope.addBlock(block_tag, node);
try loop_scope.instructions.append(mod.gpa, cond_block);
try continue_scope.setBlockBody(cond_block);
@@ -2152,7 +2157,6 @@ fn whileExpr(
if (while_full.ast.cont_expr != 0) {
_ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
}
- const is_inline = while_full.inline_token != null;
const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
_ = try loop_scope.addNode(repeat_tag, node);
@@ -2208,6 +2212,7 @@ fn whileExpr(
return mod.failTok(scope, some.token, "unused while loop label", .{});
}
}
+ const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
return finishThenElseBlock(
mod,
scope,
@@ -2224,6 +2229,7 @@ fn whileExpr(
else_info.result,
loop_block,
cond_block,
+ break_tag,
);
}
@@ -2424,6 +2430,7 @@ fn forExpr(
else_info.result,
for_block,
cond_block,
+ .@"break",
);
}
src/Module.zig
@@ -952,15 +952,11 @@ pub const Scope = struct {
/// initialized, but empty, state.
pub fn finish(gz: *GenZir) !zir.Code {
const gpa = gz.zir_code.gpa;
- const root_start = @intCast(u32, gz.zir_code.extra.items.len);
- const root_len = @intCast(u32, gz.instructions.items.len);
- try gz.zir_code.extra.appendSlice(gpa, gz.instructions.items);
+ try gz.setBlockBody(0);
return zir.Code{
.instructions = gz.zir_code.instructions.toOwnedSlice(),
.string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa),
.extra = gz.zir_code.extra.toOwnedSlice(gpa),
- .root_start = root_start,
- .root_len = root_len,
};
}
@@ -1224,11 +1220,12 @@ pub const Scope = struct {
pub fn addBreak(
gz: *GenZir,
+ tag: zir.Inst.Tag,
break_block: zir.Inst.Index,
operand: zir.Inst.Ref,
) !zir.Inst.Index {
return gz.addAsIndex(.{
- .tag = .@"break",
+ .tag = tag,
.data = .{ .@"break" = .{
.block_inst = break_block,
.operand = operand,
@@ -1236,20 +1233,6 @@ pub const Scope = struct {
});
}
- pub fn addBreakVoid(
- gz: *GenZir,
- break_block: zir.Inst.Index,
- node_index: ast.Node.Index,
- ) !zir.Inst.Index {
- return gz.addAsIndex(.{
- .tag = .break_void_node,
- .data = .{ .break_void_node = .{
- .src_node = gz.zir_code.decl.nodeIndexToRelative(node_index),
- .block_inst = break_block,
- } },
- });
- }
-
pub fn addBin(
gz: *GenZir,
tag: zir.Inst.Tag,
@@ -1323,11 +1306,11 @@ pub const Scope = struct {
/// Note that this returns a `zir.Inst.Index` not a ref.
/// Leaves the `payload_index` field undefined.
- pub fn addCondBr(gz: *GenZir, node: ast.Node.Index) !zir.Inst.Index {
+ pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
try gz.instructions.ensureCapacity(gz.zir_code.gpa, gz.instructions.items.len + 1);
const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
- .tag = .condbr,
+ .tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.zir_code.decl.nodeIndexToRelative(node),
.payload_index = undefined,
@@ -1462,6 +1445,24 @@ pub const WipZirCode = struct {
}
};
+/// Call `deinit` on the result.
+fn initAstGen(mod: *Module, decl: *Decl, arena: *Allocator) !WipZirCode {
+ var wzc: WipZirCode = .{
+ .decl = decl,
+ .arena = arena,
+ .gpa = mod.gpa,
+ };
+ // Must be a block instruction at index 0 with the root body.
+ try wzc.instructions.append(mod.gpa, .{
+ .tag = .block,
+ .data = .{ .pl_node = .{
+ .src_node = 0,
+ .payload_index = undefined,
+ } },
+ });
+ return wzc;
+}
+
/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
/// Its memory is managed with the general purpose allocator so that they
/// can be created and destroyed in response to incremental updates.
@@ -1572,10 +1573,10 @@ pub const SrcLoc = struct {
const token_starts = tree.tokens.items(.start);
return token_starts[tok_index];
},
- .node_abs => |node_index| {
+ .node_abs => |node| {
const tree = src_loc.container.file_scope.base.tree();
const token_starts = tree.tokens.items(.start);
- const tok_index = tree.firstToken(node_index);
+ const tok_index = tree.firstToken(node);
return token_starts[tok_index];
},
.byte_offset => |byte_off| {
@@ -1591,15 +1592,14 @@ pub const SrcLoc = struct {
},
.node_offset => |node_off| {
const decl = src_loc.container.decl;
- const node_index = decl.relativeToNodeIndex(node_off);
+ const node = decl.relativeToNodeIndex(node_off);
const tree = decl.container.file_scope.base.tree();
const main_tokens = tree.nodes.items(.main_token);
- const tok_index = main_tokens[node_index];
+ const tok_index = main_tokens[node];
const token_starts = tree.tokens.items(.start);
return token_starts[tok_index];
},
.node_offset_var_decl_ty => @panic("TODO"),
- .node_offset_for_cond => @panic("TODO"),
.node_offset_builtin_call_arg0 => @panic("TODO"),
.node_offset_builtin_call_arg1 => @panic("TODO"),
.node_offset_builtin_call_argn => unreachable, // Handled specially in `Sema`.
@@ -1610,7 +1610,27 @@ pub const SrcLoc = struct {
.node_offset_deref_ptr => @panic("TODO"),
.node_offset_asm_source => @panic("TODO"),
.node_offset_asm_ret_ty => @panic("TODO"),
- .node_offset_if_cond => @panic("TODO"),
+
+ .node_offset_for_cond, .node_offset_if_cond => |node_off| {
+ const decl = src_loc.container.decl;
+ const node = decl.relativeToNodeIndex(node_off);
+ const tree = decl.container.file_scope.base.tree();
+ const node_tags = tree.nodes.items(.tag);
+ const cond_expr = switch (node_tags[node]) {
+ .if_simple => tree.ifSimple(node).ast.cond_expr,
+ .@"if" => tree.ifFull(node).ast.cond_expr,
+ .while_simple => tree.whileSimple(node).ast.cond_expr,
+ .while_cont => tree.whileCont(node).ast.cond_expr,
+ .@"while" => tree.whileFull(node).ast.cond_expr,
+ .for_simple => tree.forSimple(node).ast.cond_expr,
+ .@"for" => tree.forFull(node).ast.cond_expr,
+ else => unreachable,
+ };
+ const main_tokens = tree.nodes.items(.main_token);
+ const tok_index = main_tokens[cond_expr];
+ const token_starts = tree.tokens.items(.start);
+ return token_starts[tok_index];
+ },
.node_offset_bin_op => @panic("TODO"),
.node_offset_bin_lhs => @panic("TODO"),
.node_offset_bin_rhs => @panic("TODO"),
@@ -2034,11 +2054,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
defer analysis_arena.deinit();
var code: zir.Code = blk: {
- var wip_zir_code: WipZirCode = .{
- .decl = decl,
- .arena = &analysis_arena.allocator,
- .gpa = mod.gpa,
- };
+ var wip_zir_code = try mod.initAstGen(decl, &analysis_arena.allocator);
defer wip_zir_code.deinit();
var gen_scope: Scope.GenZir = .{
@@ -2111,11 +2127,7 @@ fn astgenAndSemaFn(
var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
defer fn_type_scope_arena.deinit();
- var fn_type_wip_zir_code: WipZirCode = .{
- .decl = decl,
- .arena = &fn_type_scope_arena.allocator,
- .gpa = mod.gpa,
- };
+ var fn_type_wip_zir_code = try mod.initAstGen(decl, &fn_type_scope_arena.allocator);
defer fn_type_wip_zir_code.deinit();
var fn_type_scope: Scope.GenZir = .{
@@ -2270,7 +2282,7 @@ fn astgenAndSemaFn(
const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;
break :fn_type try fn_type_scope.addFnType(tag, return_type_inst, param_types);
};
- _ = try fn_type_scope.addUnNode(.break_flat, fn_type_inst, 0);
+ _ = try fn_type_scope.addBreak(.break_inline, 0, fn_type_inst);
// We need the memory for the Type to go into the arena for the Decl
var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
@@ -2348,12 +2360,8 @@ fn astgenAndSemaFn(
const fn_zir: zir.Code = blk: {
// We put the ZIR inside the Decl arena.
- var wip_zir_code: WipZirCode = .{
- .decl = decl,
- .arena = &decl_arena.allocator,
- .gpa = mod.gpa,
- .ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count),
- };
+ var wip_zir_code = try mod.initAstGen(decl, &decl_arena.allocator);
+ wip_zir_code.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count);
defer wip_zir_code.deinit();
var gen_scope: Scope.GenZir = .{
@@ -2559,11 +2567,7 @@ fn astgenAndSemaVarDecl(
var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
defer gen_scope_arena.deinit();
- var wip_zir_code: WipZirCode = .{
- .decl = decl,
- .arena = &gen_scope_arena.allocator,
- .gpa = mod.gpa,
- };
+ var wip_zir_code = try mod.initAstGen(decl, &gen_scope_arena.allocator);
defer wip_zir_code.deinit();
var gen_scope: Scope.GenZir = .{
@@ -2583,7 +2587,7 @@ fn astgenAndSemaVarDecl(
init_result_loc,
var_decl.ast.init_node,
);
- _ = try gen_scope.addUnNode(.break_flat, init_inst, var_decl.ast.init_node);
+ _ = try gen_scope.addBreak(.break_inline, 0, init_inst);
var code = try gen_scope.finish();
defer code.deinit(mod.gpa);
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
@@ -2611,7 +2615,7 @@ fn astgenAndSemaVarDecl(
};
defer block_scope.instructions.deinit(mod.gpa);
- const init_inst_zir_ref = try sema.root(&block_scope);
+ const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
// The result location guarantees the type coercion.
const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);
// The is_comptime in the Scope.Block guarantees the result is comptime-known.
@@ -2632,11 +2636,7 @@ fn astgenAndSemaVarDecl(
var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
defer type_scope_arena.deinit();
- var wip_zir_code: WipZirCode = .{
- .decl = decl,
- .arena = &type_scope_arena.allocator,
- .gpa = mod.gpa,
- };
+ var wip_zir_code = try mod.initAstGen(decl, &type_scope_arena.allocator);
defer wip_zir_code.deinit();
var type_scope: Scope.GenZir = .{
@@ -2647,7 +2647,7 @@ fn astgenAndSemaVarDecl(
defer type_scope.instructions.deinit(mod.gpa);
const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
- _ = try type_scope.addUnNode(.break_flat, var_type, 0);
+ _ = try type_scope.addBreak(.break_inline, 0, var_type);
var code = try type_scope.finish();
defer code.deinit(mod.gpa);
src/Sema.zig
@@ -60,14 +60,21 @@ const InnerError = Module.InnerError;
const Decl = Module.Decl;
const LazySrcLoc = Module.LazySrcLoc;
-pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {
- const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len];
+pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Index {
+ const inst_data = sema.code.instructions.items(.data)[0].pl_node;
+ const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
+ const root_body = sema.code.extra[extra.end..][0..extra.data.body_len];
return sema.analyzeBody(root_block, root_body);
}
-/// Assumes that `root_block` ends with `break_flat`.
+pub fn rootAsRef(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {
+ const break_inst = try sema.root(root_block);
+ return sema.code.instructions.items(.data)[break_inst].@"break".operand;
+}
+
+/// Assumes that `root_block` ends with `break_inline`.
pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
- const zir_inst_ref = try sema.root(root_block);
+ const zir_inst_ref = try sema.rootAsRef(root_block);
// Source location is unneeded because resolveConstValue must have already
// been successfully called when coercing the value to a type, from the
// result location.
@@ -78,17 +85,22 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
/// return type of `analyzeBody` so that we can tail call them.
/// Only appropriate to return when the instruction is known to be NoReturn
/// solely based on the ZIR tag.
-const always_noreturn: InnerError!zir.Inst.Ref = .none;
+const always_noreturn: InnerError!zir.Inst.Index = @as(zir.Inst.Index, undefined);
/// This function is the main loop of `Sema` and it can be used in two different ways:
/// * The traditional way where there are N breaks out of the block and peer type
/// resolution is done on the break operands. In this case, the `zir.Inst.Index`
/// part of the return value will be `undefined`, and callsites should ignore it,
/// finding the block result value via the block scope.
-/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_flat`
+/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline`
/// instruction. In this case, the `zir.Inst.Index` part of the return value will be
-/// the block result value. No block scope needs to be created for this strategy.
-pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !zir.Inst.Ref {
+/// the break instruction. This communicates both which block the break applies to, as
+/// well as the operand. No block scope needs to be created for this strategy.
+pub fn analyzeBody(
+ sema: *Sema,
+ block: *Scope.Block,
+ body: []const zir.Inst.Index,
+) InnerError!zir.Inst.Index {
// No tracy calls here, to avoid interfering with the tail call mechanism.
const map = block.sema.inst_map;
@@ -127,8 +139,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
.bitcast => try sema.zirBitcast(block, inst),
.bitcast_ref => try sema.zirBitcastRef(block, inst),
.bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
- .block => try sema.zirBlock(block, inst, false),
- .block_comptime => try sema.zirBlock(block, inst, true),
+ .block => try sema.zirBlock(block, inst),
.bool_not => try sema.zirBoolNot(block, inst),
.bool_and => try sema.zirBoolOp(block, inst, false),
.bool_or => try sema.zirBoolOp(block, inst, true),
@@ -227,8 +238,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
// tail call them here.
.condbr => return sema.zirCondbr(block, inst),
.@"break" => return sema.zirBreak(block, inst),
- .break_void_node => return sema.zirBreakVoidNode(block, inst),
- .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand,
+ .break_inline => return inst,
.compile_error => return sema.zirCompileError(block, inst),
.ret_coerce => return sema.zirRetTok(block, inst, true),
.ret_node => return sema.zirRetNode(block, inst),
@@ -286,13 +296,43 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
continue;
},
- // Special case: send comptime control flow back to the beginning of this block.
+ // Special case instructions to handle comptime control flow.
.repeat_inline => {
+ // Send comptime control flow back to the beginning of this block.
const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
try sema.emitBackwardBranch(block, src);
i = 0;
continue;
},
+ .block_inline => blk: {
+ // Directly analyze the block body without introducing a new block.
+ const inst_data = datas[inst].pl_node;
+ const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
+ const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ const break_inst = try sema.analyzeBody(block, inline_body);
+ const break_data = datas[break_inst].@"break";
+ if (inst == break_data.block_inst) {
+ break :blk try sema.resolveInst(break_data.operand);
+ } else {
+ return break_inst;
+ }
+ },
+ .condbr_inline => blk: {
+ const inst_data = datas[inst].pl_node;
+ const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
+ const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index);
+ const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
+ const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
+ const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
+ const inline_body = if (cond.val.toBool()) then_body else else_body;
+ const break_inst = try sema.analyzeBody(block, inline_body);
+ const break_data = datas[break_inst].@"break";
+ if (inst == break_data.block_inst) {
+ break :blk try sema.resolveInst(break_data.operand);
+ } else {
+ return break_inst;
+ }
+ },
};
if (map[inst].ty.isNoReturn())
return always_noreturn;
@@ -745,7 +785,7 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In
return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
}
-fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
+fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -783,7 +823,7 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
}
}
-fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
+fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -854,12 +894,7 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE
return sema.analyzeBlockBody(parent_block, &child_block, merges);
}
-fn zirBlock(
- sema: *Sema,
- parent_block: *Scope.Block,
- inst: zir.Inst.Index,
- is_comptime: bool,
-) InnerError!*Inst {
+fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -896,7 +931,7 @@ fn zirBlock(
},
}),
.inlining = parent_block.inlining,
- .is_comptime = is_comptime or parent_block.is_comptime,
+ .is_comptime = parent_block.is_comptime,
};
const merges = &child_block.label.?.merges;
@@ -1000,7 +1035,7 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
_ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
}
-fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
+fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -1009,22 +1044,13 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!z
return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
}
-fn zirBreakVoidNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
-
- const inst_data = sema.code.instructions.items(.data)[inst].break_void_node;
- const void_inst = try sema.mod.constVoid(sema.arena, .unneeded);
- return sema.analyzeBreak(block, inst_data.src(), inst_data.block_inst, void_inst);
-}
-
fn analyzeBreak(
sema: *Sema,
start_block: *Scope.Block,
src: LazySrcLoc,
zir_block: zir.Inst.Index,
operand: *Inst,
-) InnerError!zir.Inst.Ref {
+) InnerError!zir.Inst.Index {
var block = start_block;
while (true) {
if (block.label) |*label| {
@@ -2844,7 +2870,8 @@ fn zirBoolBr(
const tracy = trace(@src());
defer tracy.end();
- const inst_data = sema.code.instructions.items(.data)[inst].bool_br;
+ const datas = sema.code.instructions.items(.data);
+ const inst_data = datas[inst].bool_br;
const src: LazySrcLoc = .unneeded;
const lhs = try sema.resolveInst(inst_data.lhs);
const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
@@ -2856,9 +2883,9 @@ fn zirBoolBr(
}
// comptime-known left-hand side. No need for a block here; the result
// is simply the rhs expression. Here we rely on there only being 1
- // break instruction (`break_flat`).
- const zir_inst_ref = try sema.analyzeBody(parent_block, body);
- return sema.resolveInst(zir_inst_ref);
+ // break instruction (`break_inline`).
+ const break_inst = try sema.analyzeBody(parent_block, body);
+ return sema.resolveInst(datas[break_inst].@"break".operand);
}
const block_inst = try sema.arena.create(Inst.Block);
@@ -2889,8 +2916,8 @@ fn zirBoolBr(
});
_ = try lhs_block.addBr(src, block_inst, lhs_result);
- const rhs_result_zir_ref = try sema.analyzeBody(rhs_block, body);
- const rhs_result = try sema.resolveInst(rhs_result_zir_ref);
+ const rhs_break_inst = try sema.analyzeBody(rhs_block, body);
+ const rhs_result = try sema.resolveInst(datas[rhs_break_inst].@"break".operand);
_ = try rhs_block.addBr(src, block_inst, rhs_result);
const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
@@ -2959,7 +2986,7 @@ fn zirCondbr(
sema: *Sema,
parent_block: *Scope.Block,
inst: zir.Inst.Index,
-) InnerError!zir.Inst.Ref {
+) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -3008,7 +3035,7 @@ fn zirCondbr(
return always_noreturn;
}
-fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
+fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -3030,7 +3057,7 @@ fn zirRetTok(
block: *Scope.Block,
inst: zir.Inst.Index,
need_coercion: bool,
-) InnerError!zir.Inst.Ref {
+) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -3041,7 +3068,7 @@ fn zirRetTok(
return sema.analyzeRet(block, operand, src, need_coercion);
}
-fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
+fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -3058,7 +3085,7 @@ fn analyzeRet(
operand: *Inst,
src: LazySrcLoc,
need_coercion: bool,
-) InnerError!zir.Inst.Ref {
+) InnerError!zir.Inst.Index {
if (block.inlining) |inlining| {
// We are inlining a function call; rewrite the `ret` as a `break`.
try inlining.merges.results.append(sema.gpa, operand);
@@ -3244,7 +3271,7 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
try parent_block.instructions.append(sema.gpa, &block_inst.base);
}
-fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Ref {
+fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Index {
// TODO Once we have a panic function to call, call it here instead of breakpoint.
_ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
_ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
src/zir.zig
@@ -26,6 +26,8 @@ const LazySrcLoc = Module.LazySrcLoc;
/// handled by the codegen backend, and errors reported there. However for now,
/// inline assembly is not an exception.
pub const Code = struct {
+ /// There is always implicitly a `block` instruction at index 0.
+ /// This is so that `break_inline` can break from the root block.
instructions: std.MultiArrayList(Inst).Slice,
/// In order to store references to strings in fewer bytes, we copy all
/// string bytes into here. String bytes can be null. It is up to whomever
@@ -35,11 +37,6 @@ pub const Code = struct {
string_bytes: []u8,
/// The meaning of this data is determined by `Inst.Tag` value.
extra: []u32,
- /// First ZIR instruction in this `Code`.
- /// `extra` at this index contains a `Ref` for every root member.
- root_start: u32,
- /// Number of ZIR instructions in the implicit root block of the `Code`.
- root_len: u32,
/// Returns the requested data, as well as the new index which is at the start of the
/// trailers for the object.
@@ -98,17 +95,14 @@ pub const Code = struct {
.arena = &arena.allocator,
.scope = scope,
.code = code,
- .indent = 2,
+ .indent = 0,
.param_count = param_count,
};
const decl_name = scope.srcDecl().?.name;
const stderr = std.io.getStdErr().writer();
- try stderr.print("ZIR {s} {s} {{\n", .{ kind, decl_name });
-
- const root_body = code.extra[code.root_start..][0..code.root_len];
- try writer.writeBody(stderr, root_body);
-
+ try stderr.print("ZIR {s} {s} %0 ", .{ kind, decl_name });
+ try writer.writeInstToStream(stderr, 0);
try stderr.print("}} // ZIR {s} {s}\n\n", .{ kind, decl_name });
}
};
@@ -189,8 +183,11 @@ pub const Inst = struct {
/// A labeled block of code, which can return a value.
/// Uses the `pl_node` union field. Payload is `Block`.
block,
- /// Same as `block` but additionally makes the inner instructions execute at comptime.
- block_comptime,
+ /// A list of instructions which are analyzed in the parent context, without
+ /// generating a runtime block. Must terminate with an "inline" variant of
+ /// a noreturn instruction.
+ /// Uses the `pl_node` union field. Payload is `Block`.
+ block_inline,
/// Boolean AND. See also `bit_and`.
/// Uses the `pl_node` union field. Payload is `Bin`.
bool_and,
@@ -212,16 +209,12 @@ pub const Inst = struct {
/// Uses the `break` union field.
/// Uses the source information from previous instruction.
@"break",
- /// Same as `break` but has source information in the form of an AST node, and
- /// the operand is assumed to be the void value.
- /// Uses the `break_void_node` union field.
- break_void_node,
- /// Return a value from a block. This is a special form that is only valid
- /// when there is exactly 1 break from a block (this one). This instruction
- /// allows using the return value from `Sema.analyzeBody`. The block is
- /// assumed to be the direct parent of this instruction.
- /// Uses the `un_node` union field. The AST node is unused.
- break_flat,
+ /// Return a value from a block. This instruction is used as the terminator
+ /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`.
+ /// This instruction may also be used when it is known that there is only one
+ /// break instruction in a block, and the target block is the parent.
+ /// Uses the `break` union field.
+ break_inline,
/// Uses the `node` union field.
breakpoint,
/// Function call with modifier `.auto`.
@@ -270,7 +263,11 @@ pub const Inst = struct {
/// Uses the `pl_node` union field. AST node is an if, while, for, etc.
/// Payload is `CondBr`.
condbr,
- /// Special case, has no textual representation.
+ /// Same as `condbr`, except the condition is coerced to a comptime value, and
+ /// only the taken branch is analyzed. The then block and else block must
+ /// terminate with an "inline" variant of a noreturn instruction.
+ condbr_inline,
+ /// A comptime known value.
/// Uses the `const` union field.
@"const",
/// Declares the beginning of a statement. Used for debug info.
@@ -640,7 +637,7 @@ pub const Inst = struct {
.bitcast_result_ptr,
.bit_or,
.block,
- .block_comptime,
+ .block_inline,
.loop,
.bool_br_and,
.bool_br_or,
@@ -744,9 +741,9 @@ pub const Inst = struct {
=> false,
.@"break",
- .break_void_node,
- .break_flat,
+ .break_inline,
.condbr,
+ .condbr_inline,
.compile_error,
.ret_node,
.ret_tok,
@@ -1194,16 +1191,6 @@ pub const Inst = struct {
return .{ .node_offset = self.src_node };
}
},
- break_void_node: struct {
- /// Offset from Decl AST node index.
- /// `Tag` determines which kind of AST node this points to.
- src_node: i32,
- block_inst: Index,
-
- pub fn src(self: @This()) LazySrcLoc {
- return .{ .node_offset = self.src_node };
- }
- },
@"break": struct {
block_inst: Index,
operand: Ref,
@@ -1410,7 +1397,6 @@ const Writer = struct {
.err_union_payload_unsafe_ptr,
.err_union_code,
.err_union_code_ptr,
- .break_flat,
.is_non_null,
.is_null,
.is_non_null_ptr,
@@ -1438,9 +1424,11 @@ const Writer = struct {
.int => try self.writeInt(stream, inst),
.str => try self.writeStr(stream, inst),
.elided => try stream.writeAll(")"),
- .break_void_node => try self.writeBreakVoidNode(stream, inst),
.int_type => try self.writeIntType(stream, inst),
- .@"break" => try self.writeBreak(stream, inst),
+
+ .@"break",
+ .break_inline,
+ => try self.writeBreak(stream, inst),
.@"asm",
.asm_volatile,
@@ -1487,11 +1475,13 @@ const Writer = struct {
=> try self.writePlNodeCall(stream, inst),
.block,
- .block_comptime,
+ .block_inline,
.loop,
=> try self.writePlNodeBlock(stream, inst),
- .condbr => try self.writePlNodeCondBr(stream, inst),
+ .condbr,
+ .condbr_inline,
+ => try self.writePlNodeCondBr(stream, inst),
.as_node => try self.writeAs(stream, inst),
@@ -1771,13 +1761,6 @@ const Writer = struct {
return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
}
- fn writeBreakVoidNode(self: *Writer, stream: anytype, inst: Inst.Index) !void {
- const inst_data = self.code.instructions.items(.data)[inst].break_void_node;
- try self.writeInstIndex(stream, inst_data.block_inst);
- try stream.writeAll(") ");
- try self.writeSrc(stream, inst_data.src());
- }
-
fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void {
const int_type = self.code.instructions.items(.data)[inst].int_type;
const prefix: u8 = switch (int_type.signedness) {
test/stage2/test.zig
@@ -621,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void {
"hello\nhello\nhello\nhello\n",
);
+ // inline while requires the condition to be comptime known.
+ case.addError(
+ \\export fn _start() noreturn {
+ \\ var i: u32 = 0;
+ \\ inline while (i < 4) : (i += 1) print();
+ \\ assert(i == 4);
+ \\
+ \\ exit();
+ \\}
+ \\
+ \\fn print() void {
+ \\ asm volatile ("syscall"
+ \\ :
+ \\ : [number] "{rax}" (1),
+ \\ [arg1] "{rdi}" (1),
+ \\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
+ \\ [arg3] "{rdx}" (6)
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\ return;
+ \\}
+ \\
+ \\pub fn assert(ok: bool) void {
+ \\ if (!ok) unreachable; // assertion failure
+ \\}
+ \\
+ \\fn exit() noreturn {
+ \\ asm volatile ("syscall"
+ \\ :
+ \\ : [number] "{rax}" (231),
+ \\ [arg1] "{rdi}" (0)
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\ unreachable;
+ \\}
+ , &[_][]const u8{":3:21: error: unable to resolve comptime value"});
+
// Labeled blocks (no conditional branch)
case.addCompareOutput(
\\export fn _start() noreturn {