astgen: rework orelse/catch

This commit is contained in:
Andrew Kelley 2021-01-25 01:47:36 -07:00
parent 6c8985fcee
commit e9e6cc2171

View File

@ -566,12 +566,11 @@ fn labeledBlockExpr(
.block_inst = block_inst,
}),
};
setBlockResultLoc(&block_scope, rl);
defer block_scope.instructions.deinit(mod.gpa);
defer block_scope.labeled_breaks.deinit(mod.gpa);
defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
setBlockResultLoc(&block_scope, rl);
try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
if (!block_scope.label.?.used) {
@ -1337,9 +1336,6 @@ fn orelseCatchExpr(
rhs: *ast.Node,
payload_node: ?*ast.Node,
) InnerError!*zir.Inst {
if (true) {
@panic("TODO reimplement this");
}
const tree = scope.tree();
const src = tree.token_locs[op_token].start;
@ -1349,22 +1345,12 @@ fn orelseCatchExpr(
.arena = scope.arena(),
.instructions = .{},
};
setBlockResultLoc(&block_scope, rl);
defer block_scope.instructions.deinit(mod.gpa);
const block = try addZIRInstBlock(mod, scope, src, .block, .{
.instructions = undefined, // populated below
});
// Most result location types can be forwarded directly; however
// if we need to write to a pointer which has an inferred type,
// proper type inference requires peer type resolution on the if's
// branches.
const branch_rl: ResultLoc = switch (rl) {
.discard, .none, .ty, .ptr, .ref => rl,
.inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
};
// This could be a pointer or value depending on the `rl` parameter.
const operand = try expr(mod, &block_scope.base, branch_rl, lhs);
block_scope.break_count += 1;
const operand = try expr(mod, &block_scope.base, block_scope.break_result_loc, lhs);
const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
@ -1373,6 +1359,10 @@ fn orelseCatchExpr(
.else_body = undefined, // populated below
}, .{});
const block = try addZIRInstBlock(mod, scope, src, .block, .{
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
});
var then_scope: Scope.GenZIR = .{
.parent = &block_scope.base,
.decl = block_scope.decl,
@ -1383,8 +1373,7 @@ fn orelseCatchExpr(
var err_val_scope: Scope.LocalVal = undefined;
const then_sub_scope = blk: {
const payload = payload_node orelse
break :blk &then_scope.base;
const payload = payload_node orelse break :blk &then_scope.base;
const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
if (mem.eql(u8, err_name, "_"))
@ -1399,10 +1388,8 @@ fn orelseCatchExpr(
break :blk &err_val_scope.base;
};
_ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
.block = block,
.operand = try expr(mod, then_sub_scope, branch_rl, rhs),
}, .{});
block_scope.break_count += 1;
const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, rhs);
var else_scope: Scope.GenZIR = .{
.parent = &block_scope.base,
@ -1414,17 +1401,97 @@ fn orelseCatchExpr(
// This could be a pointer or value depending on `unwrap_op`.
const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
_ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
.block = block,
.operand = unwrapped_payload,
}, .{});
// All branches have been generated, add the instructions to the block.
block.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
return finishThenElseBlock(
mod,
scope,
rl,
&block_scope,
&then_scope,
&else_scope,
&condbr.positionals.then_body,
&condbr.positionals.else_body,
src,
src,
then_result,
unwrapped_payload,
block,
);
}
condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
return &block.base;
fn finishThenElseBlock(
mod: *Module,
parent_scope: *Scope,
rl: ResultLoc,
block_scope: *Scope.GenZIR,
then_scope: *Scope.GenZIR,
else_scope: *Scope.GenZIR,
then_body: *zir.Body,
else_body: *zir.Body,
then_src: usize,
else_src: usize,
then_result: *zir.Inst,
else_result: ?*zir.Inst,
block: *zir.Inst.Block,
) InnerError!*zir.Inst {
// We now have enough information to decide whether the result instruction should
// be communicated via result location pointer or break instructions.
const strat = rlStrategy(rl, block_scope);
switch (strat.tag) {
.break_void => {
if (!then_result.tag.isNoReturn()) {
_ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{
.block = block,
});
}
if (else_result) |inst| {
if (!inst.tag.isNoReturn()) {
_ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
.block = block,
});
}
} else {
_ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
.block = block,
});
}
assert(!strat.elide_store_to_block_ptr_instructions);
try copyBodyNoEliding(then_body, then_scope.*);
try copyBodyNoEliding(else_body, else_scope.*);
return &block.base;
},
.break_operand => {
if (!then_result.tag.isNoReturn()) {
_ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{
.block = block,
.operand = then_result,
});
}
if (else_result) |inst| {
if (!inst.tag.isNoReturn()) {
_ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{
.block = block,
.operand = inst,
});
}
} else {
_ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{
.block = block,
});
}
if (strat.elide_store_to_block_ptr_instructions) {
try copyBodyWithElidedStoreBlockPtr(then_body, then_scope.*);
try copyBodyWithElidedStoreBlockPtr(else_body, else_scope.*);
} else {
try copyBodyNoEliding(then_body, then_scope.*);
try copyBodyNoEliding(else_body, else_scope.*);
}
switch (rl) {
.ref => return &block.base,
else => return rvalue(mod, parent_scope, rl, &block.base),
}
},
}
}
/// Return whether the identifier names of two tokens are equal. Resolves @""
@ -1724,9 +1791,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
.arena = scope.arena(),
.instructions = .{},
};
defer block_scope.instructions.deinit(mod.gpa);
setBlockResultLoc(&block_scope, rl);
defer block_scope.instructions.deinit(mod.gpa);
const tree = scope.tree();
const if_src = tree.token_locs[if_node.if_token].start;
@ -1783,64 +1849,21 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
break :blk null;
};
// We now have enough information to decide whether the result instruction should
// be communicated via result location pointer or break instructions.
const strat = rlStrategy(rl, &block_scope);
switch (strat.tag) {
.break_void => {
if (!then_result.tag.isNoReturn()) {
_ = try addZirInstTag(mod, then_sub_scope, then_src, .break_void, .{
.block = block,
});
}
if (else_result) |inst| {
if (!inst.tag.isNoReturn()) {
_ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
.block = block,
});
}
} else {
_ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
.block = block,
});
}
assert(!strat.elide_store_to_block_ptr_instructions);
try copyBodyNoEliding(&condbr.positionals.then_body, then_scope);
try copyBodyNoEliding(&condbr.positionals.else_body, else_scope);
return &block.base;
},
.break_operand => {
if (!then_result.tag.isNoReturn()) {
_ = try addZirInstTag(mod, then_sub_scope, then_src, .@"break", .{
.block = block,
.operand = then_result,
});
}
if (else_result) |inst| {
if (!inst.tag.isNoReturn()) {
_ = try addZirInstTag(mod, else_sub_scope, else_src, .@"break", .{
.block = block,
.operand = inst,
});
}
} else {
_ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
.block = block,
});
}
if (strat.elide_store_to_block_ptr_instructions) {
try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.then_body, then_scope);
try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.else_body, else_scope);
} else {
try copyBodyNoEliding(&condbr.positionals.then_body, then_scope);
try copyBodyNoEliding(&condbr.positionals.else_body, else_scope);
}
switch (rl) {
.ref => return &block.base,
else => return rvalue(mod, scope, rl, &block.base),
}
},
}
return finishThenElseBlock(
mod,
scope,
rl,
&block_scope,
&then_scope,
&else_scope,
&condbr.positionals.then_body,
&condbr.positionals.else_body,
then_src,
else_src,
then_result,
else_result,
block,
);
}
/// Expects to find exactly 1 .store_to_block_ptr instruction.