Sema: resolveBody takes a parameter for break blocks

Previously, break instructions which wanted to break out of multiple
nesting layers did not work correctly at comptime.
This commit is contained in:
Andrew Kelley 2022-01-24 11:40:15 -07:00
parent 65576ea2ea
commit 8bb679bc6e
2 changed files with 45 additions and 36 deletions

View File

@ -487,20 +487,23 @@ pub fn deinit(sema: *Sema) void {
/// 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) CompileError!Air.Inst.Ref {
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_inst = try sema.analyzeBody(block, body);
const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
// 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) {
if (block.label) |label| {
if (label.zir_block != break_data.block_inst) {
sema.comptime_break_inst = break_inst;
return error.ComptimeBreak;
}
}
if (block.is_comptime and break_data.block_inst != body_inst) {
sema.comptime_break_inst = break_inst;
return error.ComptimeBreak;
}
return sema.resolveInst(break_data.operand);
}
@ -3508,10 +3511,13 @@ fn resolveBlockBody(
src: LazySrcLoc,
child_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,
merges: *Block.Merges,
) CompileError!Air.Inst.Ref {
if (child_block.is_comptime) {
return sema.resolveBody(child_block, body);
return sema.resolveBody(child_block, body, body_inst);
} else {
_ = try sema.analyzeBody(child_block, body);
return sema.analyzeBlockBody(parent_block, src, child_block, merges);
@ -4251,7 +4257,7 @@ fn analyzeCall(
const param_src = pl_tok.src();
const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index);
const param_body = sema.code.extra[extra.end..][0..extra.data.body_len];
const param_ty_inst = try sema.resolveBody(&child_block, param_body);
const param_ty_inst = try sema.resolveBody(&child_block, param_body, inst);
const param_ty = try sema.analyzeAsType(&child_block, param_src, param_ty_inst);
const arg_src = call_src; // TODO: better source location
const casted_arg = try sema.coerce(&child_block, param_ty, uncasted_args[arg_i], arg_src);
@ -4308,7 +4314,7 @@ fn analyzeCall(
// on parameters, we must now do the same for the return type as we just did with
// 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 = try sema.resolveBody(&child_block, fn_info.ret_ty_body);
const ret_ty_inst = try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst);
const ret_ty_src = func_src; // TODO better source location
const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
// Create a fresh inferred error set type for inline/comptime calls.
@ -4589,7 +4595,7 @@ fn analyzeCall(
}
arg_i += 1;
}
const new_func_inst = child_sema.resolveBody(&child_block, fn_info.param_body) catch |err| {
const new_func_inst = child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst) catch |err| {
// TODO look up the compile error that happened here and attach a note to it
// pointing here, at the generic instantiation callsite.
if (sema.owner_func) |owner_func| {
@ -5388,10 +5394,9 @@ fn zirFunc(
const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
extra_index += ret_ty_body.len;
var body_inst: Zir.Inst.Index = 0;
var src_locs: Zir.Inst.Func.SrcLocs = undefined;
if (extra.data.body_len != 0) {
body_inst = inst;
const has_body = extra.data.body_len != 0;
if (has_body) {
extra_index += extra.data.body_len;
src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
}
@ -5404,13 +5409,14 @@ fn zirFunc(
return sema.funcCommon(
block,
inst_data.src_node,
body_inst,
inst,
ret_ty_body,
cc,
Value.@"null",
false,
inferred_error_set,
false,
has_body,
src_locs,
null,
);
@ -5420,13 +5426,14 @@ fn funcCommon(
sema: *Sema,
block: *Block,
src_node_offset: i32,
body_inst: Zir.Inst.Index,
func_inst: Zir.Inst.Index,
ret_ty_body: []const Zir.Inst.Index,
cc: std.builtin.CallingConvention,
align_val: Value,
var_args: bool,
inferred_error_set: bool,
is_extern: bool,
has_body: bool,
src_locs: Zir.Inst.Func.SrcLocs,
opt_lib_name: ?[]const u8,
) CompileError!Air.Inst.Ref {
@ -5447,7 +5454,7 @@ fn funcCommon(
block.params.deinit(sema.gpa);
block.params = prev_params;
}
if (sema.resolveBody(block, ret_ty_body)) |ret_ty_inst| {
if (sema.resolveBody(block, ret_ty_body, func_inst)) |ret_ty_inst| {
if (sema.analyzeAsType(block, ret_ty_src, ret_ty_inst)) |ret_ty| {
break :ret_ty ret_ty;
} else |err| break :err err;
@ -5466,15 +5473,15 @@ fn funcCommon(
const mod = sema.mod;
const new_func: *Module.Fn = new_func: {
if (body_inst == 0) break :new_func undefined;
if (sema.comptime_args_fn_inst == body_inst) {
if (!has_body) break :new_func undefined;
if (sema.comptime_args_fn_inst == func_inst) {
const new_func = sema.preallocated_new_func.?;
sema.preallocated_new_func = null; // take ownership
break :new_func new_func;
}
break :new_func try sema.gpa.create(Module.Fn);
};
errdefer if (body_inst != 0) sema.gpa.destroy(new_func);
errdefer if (has_body) sema.gpa.destroy(new_func);
var maybe_inferred_error_set_node: ?*Module.Fn.InferredErrorSetListNode = null;
errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
@ -5599,21 +5606,21 @@ fn funcCommon(
);
}
if (body_inst == 0) {
if (!has_body) {
return sema.addType(fn_ty);
}
const is_inline = fn_ty.fnCallingConvention() == .Inline;
const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued;
const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == body_inst) blk: {
const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == func_inst) blk: {
break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;
} else null;
const fn_payload = try sema.arena.create(Value.Payload.Function);
new_func.* = .{
.state = anal_state,
.zir_body_inst = body_inst,
.zir_body_inst = func_inst,
.owner_decl = sema.owner_decl,
.comptime_args = comptime_args,
.lbrace_line = src_locs.lbrace_line,
@ -5660,7 +5667,7 @@ fn zirParam(
block.params = prev_params;
}
if (sema.resolveBody(block, body)) |param_ty_inst| {
if (sema.resolveBody(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;
@ -6809,7 +6816,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
// Validation above ensured these will succeed.
const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
if (operand_val.eql(item_val, operand_ty)) {
return sema.resolveBlockBody(block, src, &child_block, body, merges);
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
}
}
@ -6831,7 +6838,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
// Validation above ensured these will succeed.
const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
if (operand_val.eql(item_val, operand_ty)) {
return sema.resolveBlockBody(block, src, &child_block, body, merges);
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
}
@ -6848,18 +6855,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (Value.compare(operand_val, .gte, first_tv.val, operand_ty) and
Value.compare(operand_val, .lte, last_tv.val, operand_ty))
{
return sema.resolveBlockBody(block, src, &child_block, body, merges);
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
}
extra_index += body_len;
}
}
return sema.resolveBlockBody(block, src, &child_block, special.body, merges);
return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
}
if (scalar_cases_len + multi_cases_len == 0) {
return sema.resolveBlockBody(block, src, &child_block, special.body, merges);
return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
}
try sema.requireRuntimeBlock(block, src);
@ -10038,7 +10045,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`).
return sema.resolveBody(parent_block, body);
return sema.resolveBody(parent_block, body, inst);
}
const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
@ -10068,7 +10075,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);
const rhs_result = try sema.resolveBody(rhs_block, body, inst);
_ = try rhs_block.addBr(block_inst, rhs_result);
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
@ -12419,10 +12426,9 @@ fn zirFuncExtended(
const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
extra_index += ret_ty_body.len;
var body_inst: Zir.Inst.Index = 0;
var src_locs: Zir.Inst.Func.SrcLocs = undefined;
if (extra.data.body_len != 0) {
body_inst = inst;
const has_body = extra.data.body_len != 0;
if (has_body) {
extra_index += extra.data.body_len;
src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
}
@ -12434,13 +12440,14 @@ fn zirFuncExtended(
return sema.funcCommon(
block,
extra.data.src_node,
body_inst,
inst,
ret_ty_body,
cc,
align_val,
is_var_args,
is_inferred_error,
is_extern,
has_body,
src_locs,
lib_name,
);

View File

@ -3273,6 +3273,7 @@ fn findDeclsBody(
pub const FnInfo = struct {
param_body: []const Inst.Index,
param_body_inst: Inst.Index,
ret_ty_body: []const Inst.Index,
body: []const Inst.Index,
total_params_len: u32,
@ -3338,6 +3339,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
}
return .{
.param_body = param_body,
.param_body_inst = info.param_block,
.ret_ty_body = info.ret_ty_body,
.body = info.body,
.total_params_len = total_params_len,