Restructure handling of expressions in patterns (#7445)

Instead of maintaining a stack of pending subpatterns which might or
might not contain expressions, we mark non-nesting regions during
pattern handling that might contain an expression. The implementation
remains largely the same; the difference is that callers are expected to
end a pending expression region as soon as possible, rather than wait
for the end of the subpattern. This makes it possible to emit
non-pattern insts during pattern handling, without the risk that they
will get caught in a pending expression region further up the stack.
This commit is contained in:
Geoff Romer
2026-07-01 19:15:34 +00:00
committed by GitHub
parent e7771c2f6d
commit 11eaeeda7d
10 changed files with 99 additions and 71 deletions
+14 -8
View File
@@ -17,14 +17,14 @@
namespace Carbon::Check {
auto BeginSubpattern(Context& context) -> void {
auto BeginExprRegionForPattern(Context& context) -> void {
context.inst_block_stack().Push();
// TODO: This allocates an InstBlockId even in the case where the pattern has
// no associated expression. Find a way to avoid this.
context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd());
}
static auto PopSubpatternExpr(Context& context, SemIR::InstId result_id)
static auto PopExprRegion(Context& context, SemIR::InstId result_id)
-> SemIR::ExprRegionId {
if (context.region_stack().PeekRegion().size() > 1) {
// End the exit block with a branch to a successor block, whose contents
@@ -46,16 +46,16 @@ static auto PopSubpatternExpr(Context& context, SemIR::InstId result_id)
.result_id = result_id});
}
auto ConsumeSubpatternExpr(Context& context, SemIR::InstId result_id)
auto ConsumeExprRegionForPattern(Context& context, SemIR::InstId result_id)
-> SemIR::ExprRegionId {
auto region_id = PopSubpatternExpr(context, result_id);
auto region_id = PopExprRegion(context, result_id);
// Push an empty, unreachable region so that we can later detect the region
// has been consumed.
context.region_stack().PushUnreachableRegion();
return region_id;
}
auto EndEmptySubpattern(Context& context) -> void {
auto EndEmptyExprRegionForPattern(Context& context) -> void {
if (!context.region_stack().PeekRegion().empty()) {
CARBON_CHECK(context.inst_block_stack().PeekCurrentBlockContents().empty());
auto block_id = context.inst_block_stack().Pop();
@@ -65,13 +65,13 @@ auto EndEmptySubpattern(Context& context) -> void {
context.region_stack().PopAndDiscardRegion();
}
auto EndSubpattern(Context& context, NodeStack& node_stack) -> void {
auto EndExprRegionForPattern(Context& context, NodeStack& node_stack) -> void {
auto [node_id, maybe_expr_id] =
node_stack.PopWithNodeIdIf<Parse::NodeCategory::Expr>();
if (maybe_expr_id) {
// We formed an expression, not a pattern, so convert it to an expression
// pattern now.
auto expr_region_id = PopSubpatternExpr(context, *maybe_expr_id);
auto expr_region_id = PopExprRegion(context, *maybe_expr_id);
auto pattern_type_id =
GetPatternType(context, context.insts().Get(*maybe_expr_id).type_id());
node_stack.Push(node_id, AddInst<SemIR::ExprPattern>(
@@ -81,10 +81,16 @@ auto EndSubpattern(Context& context, NodeStack& node_stack) -> void {
} else {
// The expression region should have been consumed when forming the pattern
// instruction, so should now effectively be empty.
EndEmptySubpattern(context);
EndEmptyExprRegionForPattern(context);
}
}
auto MakeEmptyRegion(Context& context, SemIR::InstId result_id)
-> SemIR::ExprRegionId {
return context.sem_ir().expr_regions().Add(
{.block_ids = {SemIR::InstBlockId::Empty}, .result_id = result_id});
}
auto AddBindingEntityName(Context& context, SemIR::NameId name_id,
SemIR::InstId form_id, bool is_unused,
BindingPhase phase) -> SemIR::EntityNameId {