mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Model type expressions as regions (#4698)
This is a precondition for enabling the new pattern-matching subsystem to support binding patterns that have `if` expressions in the type position. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
co-authored by
Richard Smith
parent
95c9634c60
commit
a112cbde5c
+79
-17
@@ -768,6 +768,7 @@ auto Context::AddConvergenceBlockAndPush(Parse::NodeId node_id, int num_blocks)
|
||||
inst_block_stack().Pop();
|
||||
}
|
||||
inst_block_stack().Push(new_block_id);
|
||||
AddToRegion(new_block_id, node_id);
|
||||
}
|
||||
|
||||
auto Context::AddConvergenceBlockWithArgAndPush(
|
||||
@@ -787,6 +788,7 @@ auto Context::AddConvergenceBlockWithArgAndPush(
|
||||
inst_block_stack().Pop();
|
||||
}
|
||||
inst_block_stack().Push(new_block_id);
|
||||
AddToRegion(new_block_id, node_id);
|
||||
|
||||
// Acquire the result value.
|
||||
SemIR::TypeId result_type_id = insts().Get(*block_args.begin()).type_id();
|
||||
@@ -823,30 +825,90 @@ auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
|
||||
}
|
||||
}
|
||||
|
||||
auto Context::AddCurrentCodeBlockToFunction(Parse::NodeId node_id) -> void {
|
||||
CARBON_CHECK(!inst_block_stack().empty(), "no current code block");
|
||||
|
||||
if (return_scope_stack().empty()) {
|
||||
CARBON_CHECK(node_id.is_valid(),
|
||||
"No current function, but node_id not provided");
|
||||
TODO(node_id,
|
||||
auto Context::AddToRegion(SemIR::InstBlockId block_id, SemIR::LocId loc_id)
|
||||
-> void {
|
||||
if (region_stack_.empty()) {
|
||||
TODO(loc_id,
|
||||
"Control flow expressions are currently only supported inside "
|
||||
"functions.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inst_block_stack().is_current_block_reachable()) {
|
||||
// Don't include unreachable blocks in the function.
|
||||
if (block_id == SemIR::InstBlockId::Unreachable) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto function_id =
|
||||
insts()
|
||||
.GetAs<SemIR::FunctionDecl>(return_scope_stack().back().decl_id)
|
||||
.function_id;
|
||||
functions()
|
||||
.Get(function_id)
|
||||
.body_block_ids.push_back(inst_block_stack().PeekOrAdd());
|
||||
region_stack_.AppendToTop(block_id);
|
||||
}
|
||||
|
||||
auto Context::BeginSubpattern() -> void {
|
||||
inst_block_stack().Push();
|
||||
PushRegion(inst_block_stack().PeekOrAdd());
|
||||
}
|
||||
|
||||
auto Context::EndSubpatternAsExpr(SemIR::InstId result_id)
|
||||
-> SemIR::ExprRegionId {
|
||||
if (region_stack_.PeekArray().size() > 1) {
|
||||
// End the exit block with a branch to a successor block, whose contents
|
||||
// will be determined later.
|
||||
AddInst(SemIR::LocIdAndInst::NoLoc<SemIR::Branch>(
|
||||
{.target_id = inst_blocks().AddDefaultValue()}));
|
||||
} else {
|
||||
// This single-block region will be inserted as a SpliceBlock, so we don't
|
||||
// need control flow out of it.
|
||||
}
|
||||
auto block_id = inst_block_stack().Pop();
|
||||
CARBON_CHECK(block_id == region_stack_.PeekArray().back());
|
||||
|
||||
// TODO: Is it possible to validate that this region is genuinely
|
||||
// single-entry, single-exit?
|
||||
return sem_ir().expr_regions().Add(
|
||||
{.block_ids = PopRegion(), .result_id = result_id});
|
||||
}
|
||||
|
||||
auto Context::EndSubpatternAsEmpty() -> void {
|
||||
auto block_id = inst_block_stack().Pop();
|
||||
CARBON_CHECK(block_id == region_stack_.PeekArray().front());
|
||||
CARBON_CHECK(inst_blocks().Get(block_id).empty());
|
||||
region_stack_.PopArray();
|
||||
}
|
||||
|
||||
auto Context::InsertHere(SemIR::ExprRegionId region_id) -> SemIR::InstId {
|
||||
auto region = sem_ir_->expr_regions().Get(region_id);
|
||||
auto loc_id = insts().GetLocId(region.result_id);
|
||||
auto exit_block = inst_blocks().Get(region.block_ids.back());
|
||||
if (region.block_ids.size() == 1) {
|
||||
// TODO: Is it possible to avoid leaving an "orphan" block in the IR in the
|
||||
// first two cases?
|
||||
if (exit_block.size() == 0) {
|
||||
return region.result_id;
|
||||
}
|
||||
if (exit_block.size() == 1) {
|
||||
inst_block_stack_.AddInstId(exit_block.front());
|
||||
return region.result_id;
|
||||
}
|
||||
return AddInst<SemIR::SpliceBlock>(
|
||||
loc_id, {.type_id = insts().Get(region.result_id).type_id(),
|
||||
.block_id = region.block_ids.front(),
|
||||
.result_id = region.result_id});
|
||||
}
|
||||
if (region_stack_.empty()) {
|
||||
TODO(loc_id,
|
||||
"Control flow expressions are currently only supported inside "
|
||||
"functions.");
|
||||
return SemIR::ErrorInst::SingletonInstId;
|
||||
}
|
||||
AddInst(SemIR::LocIdAndInst::NoLoc<SemIR::Branch>(
|
||||
{.target_id = region.block_ids.front()}));
|
||||
inst_block_stack_.Pop();
|
||||
// TODO: this will cumulatively cost O(MN) running time for M blocks
|
||||
// at the Nth level of the stack. Figure out how to do better.
|
||||
region_stack_.AppendToTop(region.block_ids);
|
||||
auto resume_with_block_id =
|
||||
insts().GetAs<SemIR::Branch>(exit_block.back()).target_id;
|
||||
CARBON_CHECK(inst_blocks().GetOrEmpty(resume_with_block_id).empty());
|
||||
inst_block_stack_.Push(resume_with_block_id);
|
||||
AddToRegion(resume_with_block_id, loc_id);
|
||||
return region.result_id;
|
||||
}
|
||||
|
||||
auto Context::is_current_position_reachable() -> bool {
|
||||
|
||||
Reference in New Issue
Block a user