Restructure return declaration handling (#7076)

- A function with a return declaration always has exactly one
`ReturnSlotPattern`, representing the whole return declaration (whereas
previously that was omitted for value and reference returns).
- The `ReturnSlotPattern` always has a subpattern with the same form.
`OutParamPattern` already plays that role for initializing forms, and
`TuplePattern` will play that role for tuple forms. This change
introduces `ValueReturnPattern` and `RefReturnPattern` to represent
value and reference return forms.
- As before, the `ReturnSlotPattern` has a corresponding `ReturnSlot`
that represents the output that is initialized by a `return` statement.
Its structure parallels the structure of the `ReturnSlotPattern`, so we
need `ValueReturn` and `RefReturn` insts that correspond to
`ValueReturnPattern` and `RefReturnPattern`.

This is a step toward supporting generic return forms, where the
`ReturnSlotPattern`'s subpattern may be an action: this change ensures
that evaluating the action for a specific form produces the same SemIR
as if the form were concrete to begin with. More speculatively, this
should simplify the implementation of `return` statements with compound
return forms.

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
Geoff Romer
2026-04-22 23:38:21 +00:00
committed by GitHub
co-authored by Chandler Carruth
parent 3abc334c42
commit 49c7288619
40 changed files with 598 additions and 390 deletions
+8 -9
View File
@@ -236,17 +236,16 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id,
return_type_id = GetTupleType(context, {});
}
llvm::SmallVector<SemIR::InstId, 1> return_arg_ids;
for (auto return_pattern_id :
context.inst_blocks().GetOrEmpty(callee.return_patterns_id)) {
auto return_arg_id = SemIR::InstId::None;
if (callee.return_pattern_id.has_value()) {
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note,
"return type declared here");
builder.Note(return_pattern_id, IncompleteReturnTypeHere);
builder.Note(callee.return_pattern_id, IncompleteReturnTypeHere);
});
auto arg_type_id = CheckFunctionReturnPatternType(
context, loc_id, return_pattern_id, *callee_specific_id);
context, loc_id, callee.return_pattern_id, *callee_specific_id);
if (arg_type_id == SemIR::ErrorInst::TypeId) {
return_type_id = SemIR::ErrorInst::TypeId;
}
@@ -255,20 +254,20 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id,
case SemIR::InitRepr::Dependent:
// Tentatively use storage for a temporary as the return argument.
// This will be replaced if necessary when we perform initialization.
return_arg_ids.push_back(AddInst<SemIR::TemporaryStorage>(
context, loc_id, {.type_id = arg_type_id}));
return_arg_id = AddInst<SemIR::TemporaryStorage>(
context, loc_id, {.type_id = arg_type_id});
break;
case SemIR::InitRepr::None:
case SemIR::InitRepr::ByCopy:
case SemIR::InitRepr::Incomplete:
case SemIR::InitRepr::Abstract:
return_arg_ids.push_back(SemIR::InstId::None);
return_arg_id = SemIR::InstId::None;
break;
}
}
// Convert the arguments to match the parameters.
auto converted_args_id = ConvertCallArgs(
context, loc_id, callee_function.self_id, arg_ids, return_arg_ids, callee,
context, loc_id, callee_function.self_id, arg_ids, return_arg_id, callee,
*callee_specific_id, is_operator_syntax);
switch (callee.special_function_kind) {
case SemIR::Function::SpecialFunctionKind::Thunk: {