srctree

Matthew Lugg parent 64f77f32 6a87e42c 9d500bda
Merge pull request #19117 from mlugg/dbg-var-blocks

Major ZIR size optimizations & small cleanups in Sema

inlinesplit
lib/std/zig/AstGen.zig added: 424, removed: 304, total 120
@@ -1232,7 +1232,7 @@ fn suspendExpr(
suspend_scope.suspend_node = node;
defer suspend_scope.unstack();
 
const body_result = try expr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node);
const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node);
if (!gz.refIsNoReturn(body_result)) {
_ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
}
@@ -1353,7 +1353,7 @@ fn fnProtoExpr(
assert(param_type_node != 0);
var param_gz = block_scope.makeSubBlock(scope);
defer param_gz.unstack();
const param_type = try expr(&param_gz, scope, coerced_type_ri, param_type_node);
const param_type = try fullBodyExpr(&param_gz, scope, coerced_type_ri, param_type_node);
const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
_ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
const main_tokens = tree.nodes.items(.main_token);
@@ -2060,7 +2060,7 @@ fn comptimeExpr(
else
.none,
};
const block_result = try expr(&block_scope, scope, ty_only_ri, node);
const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node);
if (!gz.refIsNoReturn(block_result)) {
_ = try block_scope.addBreak(.@"break", block_inst, block_result);
}
@@ -2291,6 +2291,53 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
}
}
 
/// Similar to `expr`, but intended for use when `gz` corresponds to a body
/// which will contain only this node's code. Differs from `expr` in that if the
/// root expression is an unlabeled block, does not emit an actual block.
/// Instead, the block contents are emitted directly into `gz`.
fn fullBodyExpr(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
) InnerError!Zir.Inst.Ref {
const tree = gz.astgen.tree;
const node_tags = tree.nodes.items(.tag);
const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
var stmt_buf: [2]Ast.Node.Index = undefined;
const statements: []const Ast.Node.Index = switch (node_tags[node]) {
else => return expr(gz, scope, ri, node),
.block_two, .block_two_semicolon => if (node_datas[node].lhs == 0) s: {
break :s &.{};
} else if (node_datas[node].rhs == 0) s: {
stmt_buf[0] = node_datas[node].lhs;
break :s stmt_buf[0..1];
} else s: {
stmt_buf[0] = node_datas[node].lhs;
stmt_buf[1] = node_datas[node].rhs;
break :s stmt_buf[0..2];
},
.block, .block_semicolon => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
};
 
const lbrace = main_tokens[node];
if (token_tags[lbrace - 1] == .colon and
token_tags[lbrace - 2] == .identifier)
{
// Labeled blocks are tricky - forwarding result location information properly is non-trivial,
// plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This
// case is rare, so just treat it as a normal expression and create a nested block.
return expr(gz, scope, ri, node);
}
 
var sub_gz = gz.makeSubBlock(scope);
try blockExprStmts(&sub_gz, &sub_gz.base, statements);
 
return rvalue(gz, ri, .void_value, node);
}
 
fn blockExpr(
gz: *GenZir,
scope: *Scope,
@@ -2516,7 +2563,9 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
}
}
 
try genDefers(gz, parent_scope, scope, .normal_only);
if (noreturn_src_node == 0) {
try genDefers(gz, parent_scope, scope, .normal_only);
}
try checkUsed(gz, parent_scope, scope);
}
 
@@ -4102,7 +4151,7 @@ fn fnDecl(
assert(param_type_node != 0);
var param_gz = decl_gz.makeSubBlock(scope);
defer param_gz.unstack();
const param_type = try expr(&param_gz, params_scope, coerced_type_ri, param_type_node);
const param_type = try fullBodyExpr(&param_gz, params_scope, coerced_type_ri, param_type_node);
const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
_ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
 
@@ -4220,7 +4269,7 @@ fn fnDecl(
var ret_gz = decl_gz.makeSubBlock(params_scope);
defer ret_gz.unstack();
const ret_ref: Zir.Inst.Ref = inst: {
const inst = try expr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type);
const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type);
if (ret_gz.instructionsSlice().len == 0) {
// In this case we will send a len=0 body which can be encoded more efficiently.
break :inst inst;
@@ -4285,7 +4334,7 @@ fn fnDecl(
const lbrace_line = astgen.source_line - decl_gz.decl_line;
const lbrace_column = astgen.source_column;
 
_ = try expr(&fn_gz, params_scope, .{ .rl = .none }, body_node);
_ = try fullBodyExpr(&fn_gz, params_scope, .{ .rl = .none }, body_node);
try checkUsed(gz, &fn_gz.base, params_scope);
 
if (!fn_gz.endsWithNoReturn()) {
@@ -4471,19 +4520,19 @@ fn globalVarDecl(
 
var align_gz = block_scope.makeSubBlock(scope);
if (var_decl.ast.align_node != 0) {
const align_inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node);
const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node);
_ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
}
 
var linksection_gz = align_gz.makeSubBlock(scope);
if (var_decl.ast.section_node != 0) {
const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node);
const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node);
_ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
}
 
var addrspace_gz = linksection_gz.makeSubBlock(scope);
if (var_decl.ast.addrspace_node != 0) {
const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, coerced_addrspace_ri, var_decl.ast.addrspace_node);
const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, coerced_addrspace_ri, var_decl.ast.addrspace_node);
_ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
}
 
@@ -4532,7 +4581,7 @@ fn comptimeDecl(
};
defer decl_block.unstack();
 
const block_result = try expr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node);
const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node);
if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
_ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
}
@@ -4734,7 +4783,7 @@ fn testDecl(
const lbrace_line = astgen.source_line - decl_block.decl_line;
const lbrace_column = astgen.source_column;
 
const block_result = try expr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node);
const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node);
if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
 
// As our last action before the return, "pop" the error trace if needed
@@ -5981,7 +6030,7 @@ fn orelseCatchExpr(
break :blk &err_val_scope.base;
};
 
const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs);
const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs);
if (!else_scope.endsWithNoReturn()) {
// As our last action before the break, "pop" the error trace if needed
if (do_err_trace)
@@ -6149,7 +6198,7 @@ fn boolBinOp(
 
var rhs_scope = gz.makeSubBlock(scope);
defer rhs_scope.unstack();
const rhs = try expr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs);
const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs);
if (!gz.refIsNoReturn(rhs)) {
_ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs);
}
@@ -6293,7 +6342,7 @@ fn ifExpr(
}
};
 
const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node);
const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node);
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
if (!then_scope.endsWithNoReturn()) {
_ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
@@ -6335,7 +6384,7 @@ fn ifExpr(
break :s &else_scope.base;
}
};
const else_result = try expr(&else_scope, sub_scope, block_scope.break_result_info, else_node);
const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node);
if (!else_scope.endsWithNoReturn()) {
// As our last action before the break, "pop" the error trace if needed
if (do_err_trace)
@@ -6444,7 +6493,7 @@ fn whileExpr(
} = c: {
if (while_full.error_token) |_| {
const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
const err_union = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
break :c .{
.inst = err_union,
@@ -6452,14 +6501,14 @@ fn whileExpr(
};
} else if (while_full.payload_token) |_| {
const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
const optional = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
break :c .{
.inst = optional,
.bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
};
} else {
const cond = try expr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr);
const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr);
break :c .{
.inst = cond,
.bool_bit = cond,
@@ -6582,7 +6631,11 @@ fn whileExpr(
}
 
continue_scope.instructions_top = continue_scope.instructions.items.len;
_ = try unusedResultExpr(&continue_scope, &continue_scope.base, then_node);
{
try emitDbgNode(&continue_scope, then_node);
const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node);
_ = try addEnsureResult(&continue_scope, unused_result, then_node);
}
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
if (!continue_scope.endsWithNoReturn()) {
@@ -6626,7 +6679,7 @@ fn whileExpr(
// control flow apply to outer loops; not this one.
loop_scope.continue_block = .none;
loop_scope.break_block = .none;
const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
if (is_statement) {
_ = try addEnsureResult(&else_scope, else_result, else_node);
}
@@ -6894,7 +6947,7 @@ fn forExpr(
break :blk capture_sub_scope;
};
 
const then_result = try expr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node);
const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node);
_ = try addEnsureResult(&then_scope, then_result, then_node);
 
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
@@ -6913,7 +6966,7 @@ fn forExpr(
// control flow apply to outer loops; not this one.
loop_scope.continue_block = .none;
loop_scope.break_block = .none;
const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
if (is_statement) {
_ = try addEnsureResult(&else_scope, else_result, else_node);
}
@@ -7388,7 +7441,7 @@ fn switchExprErrUnion(
}
 
const target_expr_node = case.ast.target_expr;
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
// check capture_scope, not err_scope to avoid false positive unused error capture
try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
@@ -7849,7 +7902,7 @@ fn switchExpr(
try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
}
const target_expr_node = case.ast.target_expr;
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
try checkUsed(parent_gz, &case_scope.base, sub_scope);
if (!parent_gz.refIsNoReturn(case_result)) {
_ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
@@ -8405,7 +8458,14 @@ fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node:
try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}),
},
),
1 => .one,
1 => {
// Handle the negation here!
const result: Zir.Inst.Ref = switch (sign) {
.positive => .one,
.negative => .negative_one,
};
return rvalue(gz, ri, result, source_node);
},
else => try gz.addInt(num),
},
.big_int => |base| big: {
@@ -9752,7 +9812,7 @@ fn cImport(
defer block_scope.unstack();
 
const block_inst = try gz.makeBlockInst(.c_import, node);
const block_result = try expr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node);
const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node);
_ = try gz.addUnNode(.ensure_result_used, block_result, node);
if (!gz.refIsNoReturn(block_result)) {
_ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
@@ -9835,7 +9895,7 @@ fn callExpr(
defer arg_block.unstack();
 
// `call_inst` is reused to provide the param type.
const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
_ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
 
const body = arg_block.instructionsSlice();
@@ -10871,10 +10931,11 @@ fn rvalueInner(
.ty => |ty_inst| {
// Quickly eliminate some common, unnecessary type coercion.
const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32;
const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32;
const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32;
const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32;
const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32;
const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32;
const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32;
const as_u8 = @as(u64, @intFromEnum(Zir.Inst.Ref.u8_type)) << 32;
switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) {
as_ty | @intFromEnum(Zir.Inst.Ref.u1_type),
as_ty | @intFromEnum(Zir.Inst.Ref.u8_type),
@@ -10939,13 +11000,30 @@ fn rvalueInner(
as_ty | @intFromEnum(Zir.Inst.Ref.empty_struct_type),
as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero),
as_comptime_int | @intFromEnum(Zir.Inst.Ref.one),
as_bool | @intFromEnum(Zir.Inst.Ref.bool_true),
as_bool | @intFromEnum(Zir.Inst.Ref.bool_false),
as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one),
as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize),
as_usize | @intFromEnum(Zir.Inst.Ref.one_usize),
as_u8 | @intFromEnum(Zir.Inst.Ref.zero_u8),
as_u8 | @intFromEnum(Zir.Inst.Ref.one_u8),
as_u8 | @intFromEnum(Zir.Inst.Ref.four_u8),
as_bool | @intFromEnum(Zir.Inst.Ref.bool_true),
as_bool | @intFromEnum(Zir.Inst.Ref.bool_false),
as_void | @intFromEnum(Zir.Inst.Ref.void_value),
=> return result, // type of result is already correct
 
as_usize | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_usize,
as_u8 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u8,
as_usize | @intFromEnum(Zir.Inst.Ref.one) => return .one_usize,
as_u8 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u8,
as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero,
as_u8 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u8,
as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one,
as_u8 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u8,
as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero,
as_usize | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero_usize,
as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one,
as_usize | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one_usize,
 
// Need an explicit type coercion instruction.
else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{
.dest_type = ty_inst,
@@ -11676,7 +11754,8 @@ const GenZir = struct {
/// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
/// variables is permitted where it is usually not.
is_typeof: bool = false,
/// This is set to true for inline loops; false otherwise.
/// This is set to true for a `GenZir` of a `block_inline`, indicating that
/// exits from this block should use `break_inline` rather than `break`.
is_inline: bool = false,
c_import: bool = false,
/// How decls created in this scope should be named.
@@ -13471,7 +13550,7 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
 
fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
if (gz.is_comptime) return;
if (gz.instructions.items.len > 0) {
if (gz.instructions.items.len > gz.instructions_top) {
const astgen = gz.astgen;
const last = gz.instructions.items[gz.instructions.items.len - 1];
if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) {
@@ -13497,7 +13576,7 @@ fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
/// instructions; fix up Sema so we don't need it!
fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
const astgen = gz.astgen;
if (gz.instructions.items.len > 0 and
if (gz.instructions.items.len > gz.instructions_top and
@intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1)
{
const last = astgen.instructions.len - 1;
 
src/Autodoc.zig added: 424, removed: 304, total 120
@@ -5137,7 +5137,7 @@ fn analyzeFancyFunction(
file,
scope,
parent_src,
fn_info.body[0],
fn_info.body,
call_ctx,
);
} else {
@@ -5303,7 +5303,7 @@ fn analyzeFunction(
file,
scope,
parent_src,
fn_info.body[0],
fn_info.body,
call_ctx,
);
} else {
@@ -5350,17 +5350,10 @@ fn getGenericReturnType(
file: *File,
scope: *Scope,
parent_src: SrcLocInfo, // function decl line
body_main_block: Zir.Inst.Index,
body: []const Zir.Inst.Index,
call_ctx: ?*const CallContext,
) !DocData.Expr {
const tags = file.zir.instructions.items(.tag);
const data = file.zir.instructions.items(.data);
 
// We expect `body_main_block` to be the first instruction
// inside the function body, and for it to be a block instruction.
const pl_node = data[@intFromEnum(body_main_block)].pl_node;
const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index);
const body = file.zir.bodySlice(extra.end, extra.data.body_len);
if (body.len >= 4) {
const maybe_ret_inst = body[body.len - 4];
switch (tags[@intFromEnum(maybe_ret_inst)]) {
@@ -5676,6 +5669,42 @@ fn walkRef(
.expr = .{ .int = .{ .value = 1 } },
};
},
.negative_one => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.comptime_int_type) },
.expr = .{ .int = .{ .value = 1, .negated = true } },
};
},
.zero_usize => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.usize_type) },
.expr = .{ .int = .{ .value = 0 } },
};
},
.one_usize => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.usize_type) },
.expr = .{ .int = .{ .value = 1 } },
};
},
.zero_u8 => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.u8_type) },
.expr = .{ .int = .{ .value = 0 } },
};
},
.one_u8 => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.u8_type) },
.expr = .{ .int = .{ .value = 1 } },
};
},
.four_u8 => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.u8_type) },
.expr = .{ .int = .{ .value = 4 } },
};
},
 
.void_value => {
return DocData.WalkResult{
@@ -5707,18 +5736,6 @@ fn walkRef(
.empty_struct => {
return DocData.WalkResult{ .expr = .{ .@"struct" = &.{} } };
},
.zero_usize => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.usize_type) },
.expr = .{ .int = .{ .value = 0 } },
};
},
.one_usize => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.usize_type) },
.expr = .{ .int = .{ .value = 1 } },
};
},
.calling_convention_type => {
return DocData.WalkResult{
.typeRef = .{ .type = @intFromEnum(Ref.type_type) },
 
src/Module.zig added: 424, removed: 304, total 120
@@ -3492,6 +3492,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
@panic("TODO: update owner Decl");
}
 
const decl_inst = decl.zir_decl_index.unwrap().?;
 
const gpa = mod.gpa;
const zir = decl.getFileScope(mod).zir;
 
@@ -3563,7 +3565,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
try sema.declareDependency(.{ .src_hash = try ip.trackZir(
sema.gpa,
decl.getFileScope(mod),
decl.zir_decl_index.unwrap().?,
decl_inst,
) });
 
var block_scope: Sema.Block = .{
@@ -3580,7 +3582,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
 
const decl_bodies = decl.zirBodies(mod);
 
const result_ref = (try sema.analyzeBodyBreak(&block_scope, decl_bodies.value_body)).?.operand;
const result_ref = try sema.resolveInlineBody(&block_scope, decl_bodies.value_body, decl_inst);
// We'll do some other bits with the Sema. Clear the type target index just
// in case they analyze any type.
sema.builtin_type_target_index = .none;
@@ -3593,7 +3595,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
const address_space_src: LazySrcLoc = .{ .node_offset_var_decl_addrspace = 0 };
const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
const decl_tv = try sema.resolveInstValueAllowVariables(&block_scope, init_src, result_ref, .{
const decl_tv = try sema.resolveConstValueAllowVariables(&block_scope, init_src, result_ref, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
});
 
@@ -3709,13 +3711,13 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod)));
decl.alignment = blk: {
const align_body = decl_bodies.align_body orelse break :blk .none;
const align_ref = (try sema.analyzeBodyBreak(&block_scope, align_body)).?.operand;
break :blk try sema.resolveAlign(&block_scope, align_src, align_ref);
const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst);
break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
};
decl.@"linksection" = blk: {
const linksection_body = decl_bodies.linksection_body orelse break :blk .none;
const linksection_ref = (try sema.analyzeBodyBreak(&block_scope, linksection_body)).?.operand;
const bytes = try sema.resolveConstString(&block_scope, section_src, linksection_ref, .{
const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst);
const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{
.needed_comptime_reason = "linksection must be comptime-known",
});
if (mem.indexOfScalar(u8, bytes, 0) != null) {
@@ -3741,8 +3743,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
.constant => target_util.defaultAddressSpace(target, .global_constant),
else => unreachable,
};
const addrspace_ref = (try sema.analyzeBodyBreak(&block_scope, addrspace_body)).?.operand;
break :blk try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx);
const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst);
break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx);
};
decl.has_tv = true;
decl.analysis = .complete;
@@ -4513,7 +4515,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
inner_block.error_return_trace_index = error_return_trace_index;
 
sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
sema.analyzeFnBody(&inner_block, fn_info.body) catch |err| switch (err) {
// TODO make these unreachable instead of @panic
error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),
error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
 
src/Sema.zig added: 424, removed: 304, total 120
@@ -876,104 +876,100 @@ pub fn deinit(sema: *Sema) void {
sema.* = undefined;
}
 
/// Returns only the result from the body that is specified.
/// Only appropriate to call when it is determined at comptime that this body
/// has no peers.
fn resolveBody(
sema: *Sema,
block: *Block,
body: []const Zir.Inst.Index,
/// This is the instruction that a break instruction within `body` can
/// use to return from the body.
body_inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const break_data = (try sema.analyzeBodyBreak(block, body)) orelse
return .unreachable_value;
// For comptime control flow, we need to detect when `analyzeBody` reports
// that we need to break from an outer block. In such case we
// use Zig's error mechanism to send control flow up the stack until
// we find the corresponding block to this break.
if (block.is_comptime and break_data.block_inst != body_inst) {
sema.comptime_break_inst = break_data.inst;
return error.ComptimeBreak;
}
return try sema.resolveInst(break_data.operand);
}
 
/// Performs semantic analysis of a ZIR body which is behind a runtime condition. If comptime
/// control flow happens here, Sema will convert it to runtime control flow by introducing post-hoc
/// blocks where necessary.
fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void {
_ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
sema.analyzeBodyInner(block, body) catch |err| switch (err) {
error.ComptimeBreak => {
const zir_datas = sema.code.instructions.items(.data);
const break_data = zir_datas[@intFromEnum(sema.comptime_break_inst)].@"break";
const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
try sema.addRuntimeBreak(block, .{
.block_inst = extra.block_inst,
.operand = break_data.operand,
.inst = sema.comptime_break_inst,
});
try sema.addRuntimeBreak(block, extra.block_inst, break_data.operand);
},
else => |e| return e,
};
}
 
pub fn analyzeBody(
/// Semantically analyze a ZIR function body. It is guranteed by AstGen that such a body cannot
/// trigger comptime control flow to move above the function body.
pub fn analyzeFnBody(
sema: *Sema,
block: *Block,
body: []const Zir.Inst.Index,
) !void {
_ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
sema.analyzeBodyInner(block, body) catch |err| switch (err) {
error.ComptimeBreak => unreachable, // unexpected comptime control flow
else => |e| return e,
};
}
 
const BreakData = struct {
block_inst: Zir.Inst.Index,
operand: Zir.Inst.Ref,
inst: Zir.Inst.Index,
};
 
pub fn analyzeBodyBreak(
/// Given a ZIR body which can be exited via a `break_inline` instruction, or a non-inline body which
/// we are evaluating at comptime, semantically analyze the body and return the result from it.
/// Returns `null` if control flow did not break from this block, but instead terminated with some
/// other runtime noreturn instruction. Compile-time breaks to blocks further up the stack still
/// return `error.ComptimeBreak`. If `block.is_comptime`, this function will never return `null`.
fn analyzeInlineBody(
sema: *Sema,
block: *Block,
body: []const Zir.Inst.Index,
) CompileError!?BreakData {
const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
error.ComptimeBreak => sema.comptime_break_inst,
else => |e| return e,
};
if (block.instructions.items.len != 0 and
sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef()))
/// The index which a break instruction can target to break from this body.
break_target: Zir.Inst.Index,
) CompileError!?Air.Inst.Ref {
if (sema.analyzeBodyInner(block, body)) |_| {
return null;
} else |err| switch (err) {
error.ComptimeBreak => {},
else => |e| return e,
}
const break_inst = sema.comptime_break_inst;
const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
return BreakData{
.block_inst = extra.block_inst,
.operand = break_data.operand,
.inst = break_inst,
};
if (extra.block_inst != break_target) {
// This control flow goes further up the stack.
return error.ComptimeBreak;
}
return try sema.resolveInst(break_data.operand);
}
 
/// ZIR instructions which are always `noreturn` return this. This matches the
/// 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: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefined);
/// Like `analyzeInlineBody`, but if the body does not break with a value, returns
/// `.unreachable_value` instead of `null`. Notably, use this to evaluate an arbitrary
/// body at comptime to a single result value.
pub fn resolveInlineBody(
sema: *Sema,
block: *Block,
body: []const Zir.Inst.Index,
/// The index which a break instruction can target to break from this body.
break_target: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
return (try sema.analyzeInlineBody(block, body, break_target)) orelse .unreachable_value;
}
 
/// 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_inline`
/// instruction. In this case, the `Zir.Inst.Index` part of the return value will be
/// 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.
/// This function is the main loop of `Sema`. It analyzes a single body of ZIR instructions.
///
/// If this function returns normally, the merges of `block` were populated with all possible
/// (runtime) results of this block. Peer type resolution should be performed on the result,
/// and relevant runtime instructions written to perform necessary coercions and breaks. See
/// `resolveAnalyzedBlock`. This form of return is impossible if `block.is_comptime == true`.
///
/// Alternatively, this function may return `error.ComptimeBreak`. This indicates that comptime
/// control flow is happening, and we are breaking at comptime from a block indicated by the
/// break instruction in `sema.comptime_break_inst`. This occurs for any `break_inline`, or for a
/// standard `break` at comptime. This error is pushed up the stack until the target block is
/// reached, at which point the break operand will be fetched.
///
/// It is rare to call this function directly. Usually, you want one of the following wrappers:
/// * If the body is exited via a `break_inline`, or is being evaluated at comptime,
/// use `Sema.analyzeInlineBody` or `Sema.resolveInlineBody`.
/// * If the body is behind a fresh runtime condition, use `Sema.analyzeBodyRuntimeBreak`.
/// * If the body is an entire function body, use `Sema.analyzeFnBody`.
/// * If the body is to be generated into an AIR `block`, use `Sema.resolveBlockBody`.
/// * Otherwise, direct usage of `Sema.analyzeBodyInner` may be necessary.
fn analyzeBodyInner(
sema: *Sema,
block: *Block,
body: []const Zir.Inst.Index,
) CompileError!Zir.Inst.Index {
) CompileError!void {
// No tracy calls here, to avoid interfering with the tail call mechanism.
 
try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body);
@@ -997,7 +993,7 @@ fn analyzeBodyInner(
// the loop. The only way to break out of the loop is with a `noreturn`
// instruction.
var i: u32 = 0;
const result = while (true) {
while (true) {
crash_info.setBodyIndex(i);
const inst = body[i];
std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{
@@ -1214,14 +1210,14 @@ fn analyzeBodyInner(
// Instructions that we know to *always* be noreturn based solely on their tag.
// These functions match the return type of analyzeBody so that we can
// tail call them here.
.compile_error => break sema.zirCompileError(block, inst),
.ret_implicit => break sema.zirRetImplicit(block, inst),
.ret_node => break sema.zirRetNode(block, inst),
.ret_load => break sema.zirRetLoad(block, inst),
.ret_err_value => break sema.zirRetErrValue(block, inst),
.@"unreachable" => break sema.zirUnreachable(block, inst),
.panic => break sema.zirPanic(block, inst),
.trap => break sema.zirTrap(block, inst),
.compile_error => break try sema.zirCompileError(block, inst),
.ret_implicit => break try sema.zirRetImplicit(block, inst),
.ret_node => break try sema.zirRetNode(block, inst),
.ret_load => break try sema.zirRetLoad(block, inst),
.ret_err_value => break try sema.zirRetErrValue(block, inst),
.@"unreachable" => break try sema.zirUnreachable(block, inst),
.panic => break try sema.zirPanic(block, inst),
.trap => break try sema.zirTrap(block, inst),
// zig fmt: on
 
// This instruction never exists in an analyzed body. It exists only in the declaration
@@ -1247,7 +1243,7 @@ fn analyzeBodyInner(
.builtin_extern => try sema.zirBuiltinExtern( block, extended),
.@"asm" => try sema.zirAsm( block, extended, false),
.asm_expr => try sema.zirAsm( block, extended, true),
.typeof_peer => try sema.zirTypeofPeer( block, extended),
.typeof_peer => try sema.zirTypeofPeer( block, extended, inst),
.compile_log => try sema.zirCompileLog( extended),
.min_multi => try sema.zirMinMaxMulti( block, extended, .min),
.max_multi => try sema.zirMinMaxMulti( block, extended, .max),
@@ -1522,18 +1518,16 @@ fn analyzeBodyInner(
// Special case instructions to handle comptime control flow.
.@"break" => {
if (block.is_comptime) {
break inst; // same as break_inline
sema.comptime_break_inst = inst;
return error.ComptimeBreak;
} else {
break sema.zirBreak(block, inst);
try sema.zirBreak(block, inst);
break;
}
},
.break_inline => {
if (block.is_comptime) {
break inst;
} else {
sema.comptime_break_inst = inst;
return error.ComptimeBreak;
}
sema.comptime_break_inst = inst;
return error.ComptimeBreak;
},
.repeat => {
if (block.is_comptime) {
@@ -1548,7 +1542,10 @@ fn analyzeBodyInner(
i = 0;
continue;
} else {
break always_noreturn;
// We are definitely called by `zirLoop`, which will treat the
// fact that this body does not terminate `noreturn` as an
// implicit repeat.
break;
}
},
.repeat_inline => {
@@ -1584,13 +1581,8 @@ fn analyzeBodyInner(
child_block.instructions = block.instructions;
defer block.instructions = child_block.instructions;
 
const break_data = (try sema.analyzeBodyBreak(&child_block, inline_body)) orelse
break always_noreturn;
if (inst == break_data.block_inst) {
break :blk try sema.resolveInst(break_data.operand);
} else {
break break_data.inst;
}
const result = try sema.analyzeInlineBody(&child_block, inline_body, inst) orelse break;
break :blk result;
},
.block, .block_comptime => blk: {
if (!block.is_comptime) {
@@ -1615,13 +1607,8 @@ fn analyzeBodyInner(
child_block.instructions = block.instructions;
defer block.instructions = child_block.instructions;
 
const break_data = (try sema.analyzeBodyBreak(&child_block, inline_body)) orelse
break always_noreturn;
if (inst == break_data.block_inst) {
break :blk try sema.resolveInst(break_data.operand);
} else {
break break_data.inst;
}
const result = try sema.analyzeInlineBody(&child_block, inline_body, inst) orelse break;
break :blk result;
},
.block_inline => blk: {
// Directly analyze the block body without introducing a new block.
@@ -1634,7 +1621,12 @@ fn analyzeBodyInner(
const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
const gpa = sema.gpa;
 
const opt_break_data, const need_debug_scope = b: {
const BreakResult = struct {
block_inst: Zir.Inst.Index,
operand: Zir.Inst.Ref,
};
 
const opt_break_data: ?BreakResult, const need_debug_scope = b: {
// Create a temporary child block so that this inline block is properly
// labeled for any .restore_err_ret_index instructions
var child_block = block.makeSubBlock();
@@ -1660,11 +1652,26 @@ fn analyzeBodyInner(
child_block.instructions = block.instructions;
defer block.instructions = child_block.instructions;
 
const result = try sema.analyzeBodyBreak(&child_block, inline_body);
const break_result: ?BreakResult = if (sema.analyzeBodyInner(&child_block, inline_body)) |_| r: {
break :r null;
} else |err| switch (err) {
error.ComptimeBreak => brk_res: {
const break_inst = sema.comptime_break_inst;
const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
const break_extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
break :brk_res .{
.block_inst = break_extra.block_inst,
.operand = break_data.operand,
};
},
else => |e| return e,
};
 
if (need_debug_scope) {
_ = try sema.ensurePostHoc(block, inst);
}
break :b .{ result, need_debug_scope };
 
break :b .{ break_result, need_debug_scope };
};
 
// A runtime conditional branch that needs a post-hoc block to be
@@ -1686,13 +1693,13 @@ fn analyzeBodyInner(
// It may pass through our currently being analyzed block_inline or it
// may point directly to it. In the latter case, this modifies the
// block that we looked up in the post_hoc_blocks map above.
try sema.addRuntimeBreak(block, break_data);
try sema.addRuntimeBreak(block, break_data.block_inst, break_data.operand);
}
 
try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]);
block.instructions.items.len = block_index;
 
const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges, need_debug_scope);
const block_result = try sema.resolveAnalyzedBlock(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges, need_debug_scope);
{
// Destroy the ad-hoc block entry so that it does not interfere with
// the next iteration of comptime control flow, if any.
@@ -1703,15 +1710,19 @@ fn analyzeBodyInner(
break :blk block_result;
}
 
const break_data = opt_break_data orelse break always_noreturn;
const break_data = opt_break_data orelse break;
if (inst == break_data.block_inst) {
break :blk try sema.resolveInst(break_data.operand);
} else {
break break_data.inst;
// `comptime_break_inst` preserved from `analyzeBodyInner` above.
return error.ComptimeBreak;
}
},
.condbr => blk: {
if (!block.is_comptime) break sema.zirCondbr(block, inst);
if (!block.is_comptime) {
try sema.zirCondbr(block, inst);
break;
}
// Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
const inst_data = datas[@intFromEnum(inst)].pl_node;
const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
@@ -1728,13 +1739,9 @@ fn analyzeBodyInner(
const inline_body = if (cond.val.toBool()) then_body else else_body;
 
try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
break always_noreturn;
if (inst == break_data.block_inst) {
break :blk try sema.resolveInst(break_data.operand);
} else {
break break_data.inst;
}
 
const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
break :blk result;
},
.condbr_inline => blk: {
const inst_data = datas[@intFromEnum(inst)].pl_node;
@@ -1754,13 +1761,9 @@ fn analyzeBodyInner(
try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
const old_runtime_index = block.runtime_index;
defer block.runtime_index = old_runtime_index;
const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
break always_noreturn;
if (inst == break_data.block_inst) {
break :blk try sema.resolveInst(break_data.operand);
} else {
break break_data.inst;
}
 
const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
break :blk result;
},
.@"try" => blk: {
if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
@@ -1785,13 +1788,8 @@ fn analyzeBodyInner(
if (is_non_err_val.toBool()) {
break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
}
const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
break always_noreturn;
if (inst == break_data.block_inst) {
break :blk try sema.resolveInst(break_data.operand);
} else {
break break_data.inst;
}
const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
break :blk result;
},
.try_ptr => blk: {
if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
@@ -1811,22 +1809,22 @@ fn analyzeBodyInner(
if (is_non_err_val.toBool()) {
break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
}
const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
break always_noreturn;
if (inst == break_data.block_inst) {
break :blk try sema.resolveInst(break_data.operand);
} else {
break break_data.inst;
}
const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
break :blk result;
},
.@"defer" => blk: {
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer";
const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len);
const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
error.ComptimeBreak => sema.comptime_break_inst,
if (sema.analyzeBodyInner(block, defer_body)) |_| {
// The defer terminated noreturn - no more analysis needed.
break;
} else |err| switch (err) {
error.ComptimeBreak => {},
else => |e| return e,
};
if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
}
if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) {
return error.ComptimeBreak;
}
break :blk .void_value;
},
.defer_err_code => blk: {
@@ -1835,11 +1833,16 @@ fn analyzeBodyInner(
const defer_body = sema.code.bodySlice(extra.index, extra.len);
const err_code = try sema.resolveInst(inst_data.err_code);
map.putAssumeCapacity(extra.remapped_err_code, err_code);
const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
error.ComptimeBreak => sema.comptime_break_inst,
if (sema.analyzeBodyInner(block, defer_body)) |_| {
// The defer terminated noreturn - no more analysis needed.
break;
} else |err| switch (err) {
error.ComptimeBreak => {},
else => |e| return e,
};
if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
}
if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) {
return error.ComptimeBreak;
}
break :blk .void_value;
},
};
@@ -1847,17 +1850,15 @@ fn analyzeBodyInner(
// We're going to assume that the body itself is noreturn, so let's ensure that now
assert(block.instructions.items.len > 0);
assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef()));
break always_noreturn;
break;
}
map.putAssumeCapacity(inst, air_inst);
i += 1;
};
}
 
// We may have overwritten the capture scope due to a `repeat` instruction where
// the body had a capture; restore it now.
block.wip_capture_scope = parent_capture_scope;
 
return result;
}
 
pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
@@ -1894,7 +1895,7 @@ fn resolveConstBool(
return val.toBool();
}
 
pub fn resolveConstString(
fn resolveConstString(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -1902,6 +1903,16 @@ pub fn resolveConstString(
reason: NeededComptimeReason,
) ![]u8 {
const air_inst = try sema.resolveInst(zir_ref);
return sema.toConstString(block, src, air_inst, reason);
}
 
pub fn toConstString(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
air_inst: Air.Inst.Ref,
reason: NeededComptimeReason,
) ![]u8 {
const wanted_type = Type.slice_const_u8;
const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
@@ -2193,9 +2204,8 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
return val;
}
 
/// Returns a compile error if the value has tag `variable`. See `resolveInstValue` for
/// a function that does not.
pub fn resolveInstConst(
/// Returns a compile error if the value has tag `variable`.
fn resolveInstConst(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -2211,15 +2221,13 @@ pub fn resolveInstConst(
}
 
/// Value Tag may be `undef` or `variable`.
/// See `resolveInstConst` for an alternative.
pub fn resolveInstValueAllowVariables(
pub fn resolveConstValueAllowVariables(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
air_ref: Air.Inst.Ref,
reason: NeededComptimeReason,
) CompileError!TypedValue {
const air_ref = try sema.resolveInst(zir_ref);
const val = try sema.resolveValueAllowVariables(air_ref) orelse {
return sema.failWithNeededComptime(block, src, reason);
};
@@ -2616,7 +2624,7 @@ fn reparentOwnedErrorMsg(
 
const align_ty = Type.u29;
 
fn analyzeAsAlign(
pub fn analyzeAsAlign(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -2654,7 +2662,7 @@ fn validateAlignAllowZero(
return Alignment.fromNonzeroByteUnits(alignment);
}
 
pub fn resolveAlign(
fn resolveAlign(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -3054,7 +3062,7 @@ fn zirEnumDecl(
defer enum_block.instructions.deinit(sema.gpa);
 
if (body.len != 0) {
try sema.analyzeBody(&enum_block, body);
_ = try sema.analyzeInlineBody(&enum_block, body, inst);
}
 
if (tag_type_ref != .none) {
@@ -5597,7 +5605,7 @@ fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
return Air.internedToRef((try sema.mod.floatValue(Type.comptime_float, number)).toIntern());
}
 
fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
 
@@ -5650,7 +5658,7 @@ fn zirCompileLog(
return .void_value;
}
 
fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = inst_data.src();
const msg_inst = try sema.resolveInst(inst_data.operand);
@@ -5663,16 +5671,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.I
return sema.fail(block, src, "encountered @panic at comptime", .{});
}
try sema.panicWithMsg(block, src, coerced_msg, .@"@panic");
return always_noreturn;
}
 
fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
const src = LazySrcLoc.nodeOffset(src_node);
if (block.is_comptime)
return sema.fail(block, src, "encountered @trap at comptime", .{});
_ = try block.addNoOp(.trap);
return always_noreturn;
}
 
fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -5726,7 +5732,8 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError
var loop_block = child_block.makeSubBlock();
defer loop_block.instructions.deinit(gpa);
 
try sema.analyzeBody(&loop_block, body);
// Use `analyzeBodyInner` directly to push any comptime control flow up the stack.
try sema.analyzeBodyInner(&loop_block, body);
 
const loop_block_len = loop_block.instructions.items.len;
if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(mod)) {
@@ -5742,7 +5749,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError
);
sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items));
}
return sema.analyzeBlockBody(parent_block, src, &child_block, merges, false);
return sema.resolveAnalyzedBlock(parent_block, src, &child_block, merges, false);
}
 
fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -5785,8 +5792,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
};
defer child_block.instructions.deinit(gpa);
 
// Ignore the result, all the relevant operations have written to c_import_buf already.
_ = try sema.analyzeBodyBreak(&child_block, body);
_ = try sema.analyzeInlineBody(&child_block, body, inst);
 
var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err|
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
@@ -5916,6 +5922,9 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_compt
return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
}
 
/// Semantically analyze the given ZIR body, emitting any resulting runtime code into the AIR block
/// specified by `child_block` if necessary (and emitting this block into `parent_block`).
/// TODO: `merges` is known from `child_block`, remove this parameter.
fn resolveBlockBody(
sema: *Sema,
parent_block: *Block,
@@ -5928,12 +5937,12 @@ fn resolveBlockBody(
merges: *Block.Merges,
) CompileError!Air.Inst.Ref {
if (child_block.is_comptime) {
return sema.resolveBody(child_block, body, body_inst);
return sema.resolveInlineBody(child_block, body, body_inst);
} else {
var need_debug_scope = false;
child_block.need_debug_scope = &need_debug_scope;
if (sema.analyzeBodyInner(child_block, body)) |_| {
return sema.analyzeBlockBody(parent_block, src, child_block, merges, need_debug_scope);
return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope);
} else |err| switch (err) {
error.ComptimeBreak => {
// Comptime control flow is happening, however child_block may still contain
@@ -5970,7 +5979,12 @@ fn resolveBlockBody(
}
}
 
fn analyzeBlockBody(
/// After a body corresponding to an AIR `block` has been analyzed, this function places them into
/// the block pointed at by `merges.block_inst` if necessary, or the block may be elided in favor of
/// inlining the instructions directly into the parent block. Either way, it considers all merges of
/// this block, and combines them appropriately using peer type resolution, returning the final
/// value of the block.
fn resolveAnalyzedBlock(
sema: *Sema,
parent_block: *Block,
src: LazySrcLoc,
@@ -6360,7 +6374,7 @@ fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) Co
});
}
 
fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
 
@@ -6386,7 +6400,7 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError
block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
block.runtime_loop = start_block.runtime_loop;
}
return inst;
return;
}
}
block = block.parent.?;
@@ -7096,7 +7110,7 @@ const CallArgsInfo = union(enum) {
// Give the arg its result type
sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(param_ty.toIntern()));
// Resolve the arg!
const uncoerced_arg = try sema.resolveBody(block, arg_body, zir_call.call_inst);
const uncoerced_arg = try sema.resolveInlineBody(block, arg_body, zir_call.call_inst);
 
if (sema.typeOf(uncoerced_arg).zigTypeTag(mod) == .NoReturn) {
// This terminates resolution of arguments. The caller should
@@ -7539,7 +7553,7 @@ fn analyzeCall(
// each of the parameters, resolving the return type and providing it to the child
// `Sema` so that it can be used for the `ret_ptr` instruction.
const ret_ty_inst = if (fn_info.ret_ty_body.len != 0)
try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip))
try sema.resolveInlineBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip))
else
try sema.resolveInst(fn_info.ret_ty_ref);
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
@@ -7608,11 +7622,11 @@ fn analyzeCall(
}
 
const result = result: {
sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) {
error.ComptimeReturn => break :result inlining.comptime_result,
else => |e| return e,
};
break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges, false);
break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, false);
};
 
if (!is_comptime_call and !block.is_typeof and
@@ -7791,7 +7805,7 @@ fn analyzeInlineCallArg(
const param_ty = param_ty: {
const raw_param_ty = func_ty_info.param_types.get(ip)[arg_i.*];
if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty;
const param_ty_inst = try ics.callee().resolveBody(param_block, param_body, inst);
const param_ty_inst = try ics.callee().resolveInlineBody(param_block, param_body, inst);
const param_ty = try ics.callee().analyzeAsType(param_block, param_src, param_ty_inst);
break :param_ty param_ty.toIntern();
};
@@ -8026,7 +8040,7 @@ fn instantiateGenericCall(
child_sema.generic_call_decl = prev_generic_call_decl;
}
 
const param_ty_inst = try child_sema.resolveBody(&child_block, param_ty_body, param_inst);
const param_ty_inst = try child_sema.resolveInlineBody(&child_block, param_ty_body, param_inst);
break :param_ty try child_sema.analyzeAsType(&child_block, param_data.src(), param_ty_inst);
},
else => unreachable,
@@ -8118,7 +8132,7 @@ fn instantiateGenericCall(
 
// We've already handled parameters, so don't resolve the whole body. Instead, just
// do the instructions after the params (i.e. the func itself).
const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body[args_info.count()..], fn_info.param_body_inst);
const new_func_inst = try child_sema.resolveInlineBody(&child_block, fn_info.param_body[args_info.count()..], fn_info.param_body_inst);
const callee_index = (child_sema.resolveConstDefinedValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable).toIntern();
 
const callee = mod.funcInfo(callee_index);
@@ -9176,7 +9190,7 @@ fn resolveGenericBody(
sema.generic_call_decl = prev_generic_call_decl;
}
 
const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err;
const uncasted = sema.resolveInlineBody(block, body, func_inst) catch |err| break :err err;
const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err;
const val = sema.resolveConstDefinedValue(block, src, result, reason) catch |err| break :err err;
return val;
@@ -9810,7 +9824,7 @@ fn zirParam(
sema.generic_call_decl = prev_generic_call_decl;
}
 
if (sema.resolveBody(block, body, inst)) |param_ty_inst| {
if (sema.resolveInlineBody(block, body, inst)) |param_ty_inst| {
if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| {
break :param_ty param_ty;
} else |err| break :err err;
@@ -11494,6 +11508,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
sub_block.runtime_loop = null;
sub_block.runtime_cond = mod.declPtr(child_block.src_decl).toSrcLoc(main_operand_src, mod);
sub_block.runtime_index.increment();
sub_block.need_debug_scope = null; // this body is emitted regardless
defer sub_block.instructions.deinit(gpa);
 
try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
@@ -11556,7 +11571,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
 
return sema.analyzeBlockBody(block, main_src, &child_block, merges, false);
return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false);
}
 
fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
@@ -12178,7 +12193,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
false,
);
 
return sema.analyzeBlockBody(block, src, &child_block, merges, false);
return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
}
 
const SpecialProng = struct {
@@ -12229,6 +12244,7 @@ fn analyzeSwitchRuntimeBlock(
case_block.runtime_loop = null;
case_block.runtime_cond = mod.declPtr(child_block.src_decl).toSrcLoc(operand_src, mod);
case_block.runtime_index.increment();
case_block.need_debug_scope = null; // this body is emitted regardless
defer case_block.instructions.deinit(gpa);
 
var extra_index: usize = special.end;
@@ -18602,7 +18618,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
};
defer child_block.instructions.deinit(sema.gpa);
 
const operand = try sema.resolveBody(&child_block, body, inst);
const operand = try sema.resolveInlineBody(&child_block, body, inst);
const operand_ty = sema.typeOf(operand);
if (operand_ty.isGenericPoison()) return error.GenericPoison;
return Air.internedToRef(operand_ty.toIntern());
@@ -18657,6 +18673,7 @@ fn zirTypeofPeer(
sema: *Sema,
block: *Block,
extended: Zir.Inst.Extended.InstData,
inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -18681,7 +18698,7 @@ fn zirTypeofPeer(
};
defer child_block.instructions.deinit(sema.gpa);
// Ignore the result, we only care about the instructions in `args`.
_ = try sema.analyzeBodyBreak(&child_block, body);
_ = try sema.analyzeInlineBody(&child_block, body, inst);
 
const args = sema.code.refSlice(extra.end, extended.small);
 
@@ -18748,7 +18765,7 @@ 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_inline`).
const rhs_result = try sema.resolveBody(parent_block, body, inst);
const rhs_result = try sema.resolveInlineBody(parent_block, body, inst);
if (sema.typeOf(rhs_result).isNoReturn(mod)) {
return rhs_result;
}
@@ -18782,7 +18799,7 @@ fn zirBoolBr(
const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
_ = try lhs_block.addBr(block_inst, lhs_result);
 
const rhs_result = try sema.resolveBody(rhs_block, body, inst);
const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst);
const rhs_noret = sema.typeOf(rhs_result).isNoReturn(mod);
const coerced_rhs_result = if (!rhs_noret) rhs: {
const coerced_result = try sema.coerce(rhs_block, Type.bool, rhs_result, rhs_src);
@@ -18933,7 +18950,7 @@ fn zirCondbr(
sema: *Sema,
parent_block: *Block,
inst: Zir.Inst.Index,
) CompileError!Zir.Inst.Index {
) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
 
@@ -18952,8 +18969,7 @@ fn zirCondbr(
const body = if (cond_val.toBool()) then_body else else_body;
 
try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
// We use `analyzeBodyInner` since we want to propagate any possible
// `error.ComptimeBreak` to the caller.
// We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller.
return sema.analyzeBodyInner(parent_block, body);
}
 
@@ -18965,6 +18981,7 @@ fn zirCondbr(
sub_block.runtime_loop = null;
sub_block.runtime_cond = mod.declPtr(parent_block.src_decl).toSrcLoc(cond_src, mod);
sub_block.runtime_index.increment();
sub_block.need_debug_scope = null; // this body is emitted regardless
defer sub_block.instructions.deinit(gpa);
 
try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
@@ -19002,7 +19019,6 @@ fn zirCondbr(
});
sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
return always_noreturn;
}
 
fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -19027,14 +19043,15 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
}
// We can analyze the body directly in the parent block because we know there are
// no breaks from the body possible, and that the body is noreturn.
return sema.resolveBody(parent_block, body, inst);
try sema.analyzeBodyInner(parent_block, body);
return .unreachable_value;
}
 
var sub_block = parent_block.makeSubBlock();
defer sub_block.instructions.deinit(sema.gpa);
 
// This body is guaranteed to end with noreturn and has no breaks.
_ = try sema.analyzeBodyInner(&sub_block, body);
try sema.analyzeBodyInner(&sub_block, body);
 
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
sub_block.instructions.items.len);
@@ -19074,14 +19091,15 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
}
// We can analyze the body directly in the parent block because we know there are
// no breaks from the body possible, and that the body is noreturn.
return sema.resolveBody(parent_block, body, inst);
try sema.analyzeBodyInner(parent_block, body);
return .unreachable_value;
}
 
var sub_block = parent_block.makeSubBlock();
defer sub_block.instructions.deinit(sema.gpa);
 
// This body is guaranteed to end with noreturn and has no breaks.
_ = try sema.analyzeBodyInner(&sub_block, body);
try sema.analyzeBodyInner(&sub_block, body);
 
const operand_ty = sema.typeOf(operand);
const ptr_info = operand_ty.ptrInfo(mod);
@@ -19156,13 +19174,13 @@ fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*Label
return labeled_block;
}
 
// A `break` statement is inside a runtime condition, but trying to
// break from an inline loop. In such case we must convert it to
// a runtime break.
fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void {
const labeled_block = try sema.ensurePostHoc(child_block, break_data.block_inst);
/// A `break` statement is inside a runtime condition, but trying to
/// break from an inline loop. In such case we must convert it to
/// a runtime break.
fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void {
const labeled_block = try sema.ensurePostHoc(child_block, block_inst);
 
const operand = try sema.resolveInst(break_data.operand);
const operand = try sema.resolveInst(break_operand);
const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
 
try labeled_block.label.merges.results.append(sema.gpa, operand);
@@ -19176,7 +19194,7 @@ fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !voi
}
}
 
fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
const src = inst_data.src();
 
@@ -19193,14 +19211,13 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
},
else => |e| return e,
};
return always_noreturn;
}
 
fn zirRetErrValue(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
) CompileError!Zir.Inst.Index {
) CompileError!void {
const mod = sema.mod;
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
@@ -19219,7 +19236,7 @@ fn zirRetImplicit(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
) CompileError!Zir.Inst.Index {
) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
 
@@ -19234,7 +19251,7 @@ fn zirRetImplicit(
} else {
try block.addUnreachable(r_brace_src, false);
}
return always_noreturn;
return;
}
 
const operand = try sema.resolveInst(inst_data.operand);
@@ -19265,7 +19282,7 @@ fn zirRetImplicit(
return sema.analyzeRet(block, operand, r_brace_src, r_brace_src);
}
 
fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
 
@@ -19276,7 +19293,7 @@ fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
return sema.analyzeRet(block, operand, src, .{ .node_offset_return_operand = inst_data.src_node });
}
 
fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
 
@@ -19295,7 +19312,6 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
}
 
_ = try block.addUnOp(.ret_load, ret_ptr);
return always_noreturn;
}
 
fn retWithErrTracing(
@@ -19305,12 +19321,12 @@ fn retWithErrTracing(
is_non_err: Air.Inst.Ref,
ret_tag: Air.Inst.Tag,
operand: Air.Inst.Ref,
) CompileError!Zir.Inst.Index {
) CompileError!void {
const mod = sema.mod;
const need_check = switch (is_non_err) {
.bool_true => {
_ = try block.addUnOp(ret_tag, operand);
return always_noreturn;
return;
},
.bool_false => false,
else => true,
@@ -19326,7 +19342,7 @@ fn retWithErrTracing(
if (!need_check) {
try sema.callBuiltin(block, src, return_err_fn, .never_inline, &args, .@"error return");
_ = try block.addUnOp(ret_tag, operand);
return always_noreturn;
return;
}
 
var then_block = block.makeSubBlock();
@@ -19353,8 +19369,6 @@ fn retWithErrTracing(
.operand = is_non_err,
.payload = cond_br_payload,
} } });
 
return always_noreturn;
}
 
fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
@@ -19481,7 +19495,7 @@ fn analyzeRet(
uncasted_operand: Air.Inst.Ref,
src: LazySrcLoc,
operand_src: LazySrcLoc,
) CompileError!Zir.Inst.Index {
) CompileError!void {
// Special case for returning an error to an inferred error set; we need to
// add the error tag to the inferred error set of the in-scope function, so
// that the coercion below works correctly.
@@ -19513,7 +19527,7 @@ fn analyzeRet(
try inlining.merges.results.append(sema.gpa, operand);
try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?);
try inlining.merges.src_locs.append(sema.gpa, operand_src);
return always_noreturn;
return;
} else if (block.is_comptime) {
return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
} else if (sema.func_is_naked) {
@@ -19538,8 +19552,6 @@ fn analyzeRet(
}
 
_ = try block.addUnOp(air_tag, operand);
 
return always_noreturn;
}
 
fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
@@ -19616,7 +19628,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
extra_i += 1;
break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer);
break :blk try sema.resolveAddressSpace(block, addrspace_src, ref, .pointer);
} else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic;
 
const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: {
@@ -35737,7 +35749,7 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
} else {
const body = zir.bodySlice(extra_index, backing_int_body_len);
const ty_ref = try sema.resolveBody(&block, body, zir_index);
const ty_ref = try sema.resolveInlineBody(&block, body, zir_index);
break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
}
};
@@ -36618,7 +36630,7 @@ fn semaStructFields(
assert(zir_field.type_body_len != 0);
const body = zir.bodySlice(extra_index, zir_field.type_body_len);
extra_index += body.len;
const ty_ref = try sema.resolveBody(&block_scope, body, zir_index);
const ty_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) {
error.NeededSourceLocation => {
const ty_src = mod.fieldSrcLoc(decl_index, .{
@@ -36704,7 +36716,7 @@ fn semaStructFields(
if (zir_field.align_body_len > 0) {
const body = zir.bodySlice(extra_index, zir_field.align_body_len);
extra_index += body.len;
const align_ref = try sema.resolveBody(&block_scope, body, zir_index);
const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
const field_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
error.NeededSourceLocation => {
const align_src = mod.fieldSrcLoc(decl_index, .{
@@ -36854,7 +36866,7 @@ fn semaStructFieldInits(
try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
sema.inst_map.putAssumeCapacity(zir_index, type_ref);
 
const init = try sema.resolveBody(&block_scope, body, zir_index);
const init = try sema.resolveInlineBody(&block_scope, body, zir_index);
const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {
error.NeededSourceLocation => {
const init_src = mod.fieldSrcLoc(decl_index, .{
@@ -36971,7 +36983,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
defer assert(block_scope.instructions.items.len == 0);
 
if (body.len != 0) {
try sema.analyzeBody(&block_scope, body);
_ = try sema.analyzeInlineBody(&block_scope, body, zir_index);
}
 
for (comptime_mutable_decls.items) |ct_decl_index| {
@@ -37914,15 +37926,25 @@ pub const AddressSpaceContext = enum {
pointer,
};
 
pub fn analyzeAddressSpace(
fn resolveAddressSpace(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
ctx: AddressSpaceContext,
) !std.builtin.AddressSpace {
const mod = sema.mod;
const air_ref = try sema.resolveInst(zir_ref);
return sema.analyzeAsAddressSpace(block, src, air_ref, ctx);
}
 
pub fn analyzeAsAddressSpace(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
air_ref: Air.Inst.Ref,
ctx: AddressSpaceContext,
) !std.builtin.AddressSpace {
const mod = sema.mod;
const coerced = try sema.coerce(block, Type.fromInterned(.address_space_type), air_ref, src);
const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{
.needed_comptime_reason = "address space must be comptime-known",
 
src/codegen/llvm.zig added: 424, removed: 304, total 120
@@ -5903,10 +5903,10 @@ pub const FuncGen = struct {
_ = try self.wip.brCond(cond, then_block, else_block);
 
self.wip.cursor = .{ .block = then_block };
try self.genBody(then_body);
try self.genBodyDebugScope(then_body);
 
self.wip.cursor = .{ .block = else_block };
try self.genBody(else_body);
try self.genBodyDebugScope(else_body);
 
// No need to reset the insert cursor since this instruction is noreturn.
return .none;
@@ -5987,7 +5987,7 @@ pub const FuncGen = struct {
_ = try fg.wip.brCond(is_err, return_block, continue_block);
 
fg.wip.cursor = .{ .block = return_block };
try fg.genBody(body);
try fg.genBodyDebugScope(body);
 
fg.wip.cursor = .{ .block = continue_block };
}
@@ -6060,13 +6060,13 @@ pub const FuncGen = struct {
}
 
self.wip.cursor = .{ .block = case_block };
try self.genBody(case_body);
try self.genBodyDebugScope(case_body);
}
 
self.wip.cursor = .{ .block = else_block };
const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
if (else_body.len != 0) {
try self.genBody(else_body);
try self.genBodyDebugScope(else_body);
} else {
_ = try self.wip.@"unreachable"();
}
@@ -6085,7 +6085,7 @@ pub const FuncGen = struct {
_ = try self.wip.br(loop_block);
 
self.wip.cursor = .{ .block = loop_block };
try self.genBody(body);
try self.genBodyDebugScope(body);
 
// TODO instead of this logic, change AIR to have the property that
// every block is guaranteed to end with a noreturn instruction.