Allow incomplete return types in function declarations. (#3875)

Move completeness check to the point where the function is defined or
first called. This means we also defer deciding whether the function has
a return slot until that point. Instead of storing a return slot per
function, store the location of the return storage, which may or may not
be used, and compute and store a separate flag saying whether to use it
at the point of first use or definition.

This is the final piece in supporting simple `Make` functions in classes
as a replacement for constructors.
This commit is contained in:
Richard Smith
2024-04-11 07:23:56 +00:00
committed by GitHub
parent 28ceb4dd4e
commit 1b335402d1
173 changed files with 980 additions and 615 deletions
+61 -1
View File
@@ -294,6 +294,28 @@ static auto CheckIsAllowedRedecl(Context& context, SemIR::LocId loc_id,
}
}
// Returns the return slot usage for a function given the computed usage for two
// different declarations of the function.
static auto MergeReturnSlot(SemIR::Function::ReturnSlot a,
SemIR::Function::ReturnSlot b)
-> SemIR::Function::ReturnSlot {
if (a == SemIR::Function::ReturnSlot::NotComputed) {
return b;
}
if (b == SemIR::Function::ReturnSlot::NotComputed) {
return a;
}
if (a == SemIR::Function::ReturnSlot::Error) {
return b;
}
if (b == SemIR::Function::ReturnSlot::Error) {
return a;
}
CARBON_CHECK(a == b)
<< "Different return slot usage computed for the same function.";
return a;
}
auto MergeFunctionRedecl(Context& context, SemIR::LocId loc_id,
SemIR::Function& new_function, bool new_is_import,
bool new_is_definition,
@@ -315,8 +337,11 @@ auto MergeFunctionRedecl(Context& context, SemIR::LocId loc_id,
prev_function.implicit_param_refs_id = new_function.implicit_param_refs_id;
prev_function.param_refs_id = new_function.param_refs_id;
prev_function.return_type_id = new_function.return_type_id;
prev_function.return_slot_id = new_function.return_slot_id;
prev_function.return_storage_id = new_function.return_storage_id;
}
// The new function might have return slot information if it was imported.
prev_function.return_slot =
MergeReturnSlot(prev_function.return_slot, new_function.return_slot);
if ((prev_import_ir_inst_id.is_valid() && !new_is_import) ||
(prev_function.is_extern && !new_function.is_extern)) {
prev_function.is_extern = new_function.is_extern;
@@ -327,4 +352,39 @@ auto MergeFunctionRedecl(Context& context, SemIR::LocId loc_id,
return true;
}
auto CheckFunctionReturnType(Context& context, SemIRLoc loc,
SemIR::Function& function) -> void {
// If we have already checked the return type, we have nothing to do.
if (function.return_slot != SemIR::Function::ReturnSlot::NotComputed) {
return;
}
if (!function.return_type_id.is_valid()) {
// Implicit `-> ()` has no return slot.
function.return_slot = SemIR::Function::ReturnSlot::Absent;
return;
}
// Check the return type is complete. Only diagnose incompleteness if we've
// not already done so.
auto diagnose_incomplete_return_type = [&] {
CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error,
"Function returns incomplete type `{0}`.", SemIR::TypeId);
return context.emitter().Build(loc, IncompleteTypeInFunctionReturnType,
function.return_type_id);
};
if (!context.TryToCompleteType(
function.return_type_id,
function.return_slot == SemIR::Function::ReturnSlot::Error
? std::nullopt
: std::optional(diagnose_incomplete_return_type))) {
function.return_slot = SemIR::Function::ReturnSlot::Error;
} else if (SemIR::GetInitRepr(context.sem_ir(), function.return_type_id)
.has_return_slot()) {
function.return_slot = SemIR::Function::ReturnSlot::Present;
} else {
function.return_slot = SemIR::Function::ReturnSlot::Absent;
}
}
} // namespace Carbon::Check