Files
carbon-lang/toolchain/check/function.cpp
T
Jon Ross-Perkins 4923445e3a Drop Singleton from ErrorInst::SingletonInstId and similar (#5304)
We frequently want to operate on singletons. Per discussion, drop
`Singleton` to make the code shorter.

This started off as wanting to write `inst_id.is_error()`, but the
dependency relationship between ids.h and singleton_insts.h would
require some kind of delayed evaluation to allow the implementation to
remain in headers (which I suspect is helpful to have for inlining). I
could have added something like `IsErrorInst`, forward declared in ids.h
and defined in singleton_insts.h (which would always be included by
typed_insts.h), but the template approach felt like a decent balance
between (a) removing the boilerplate `::SingletonInstId`, (b)
understandability, (c) still visually mirroring if we immediately return
a singleton, and (d) flexibility for more than just `ErrorInst`. But TBH
I'd probably still have written `is_error()` if it didn't require
addressing the cross-header cycle.

Then I tried `SemIR::InstId::Is<SemIR::ErrorInst>`, which generally
worked with types but generated the complaint that it didn't shorten
*all* singleton uses. So pulling back on `::Is`, and instead just
dropping `Singleton`.
2025-04-15 22:40:29 +00:00

112 lines
4.9 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/check/function.h"
#include "toolchain/check/merge.h"
#include "toolchain/check/type_completion.h"
#include "toolchain/sem_ir/ids.h"
namespace Carbon::Check {
auto CheckFunctionTypeMatches(Context& context,
const SemIR::Function& new_function,
const SemIR::Function& prev_function,
SemIR::SpecificId prev_specific_id,
bool check_syntax, bool check_self) -> bool {
// TODO: When check_syntax is false, the functions should be allowed to have
// different signatures as long as we can synthesize a suitable thunk. i.e.,
// when there's an implicit conversion from the original parameter types to
// the overriding parameter types, and from the overriding return type to the
// original return type.
// Also, build that thunk.
if (!CheckRedeclParamsMatch(context, DeclParams(new_function),
DeclParams(prev_function), prev_specific_id,
/*diagnose=*/true, check_syntax, check_self)) {
return false;
}
// TODO: Pass a specific ID for `prev_function` instead of substitutions and
// use it here.
auto new_return_type_id =
new_function.GetDeclaredReturnType(context.sem_ir());
auto prev_return_type_id =
prev_function.GetDeclaredReturnType(context.sem_ir(), prev_specific_id);
if (new_return_type_id == SemIR::ErrorInst::TypeId ||
prev_return_type_id == SemIR::ErrorInst::TypeId) {
return false;
}
if (!context.types().AreEqualAcrossDeclarations(new_return_type_id,
prev_return_type_id)) {
CARBON_DIAGNOSTIC(
FunctionRedeclReturnTypeDiffers, Error,
"function redeclaration differs because return type is {0}",
SemIR::TypeId);
CARBON_DIAGNOSTIC(
FunctionRedeclReturnTypeDiffersNoReturn, Error,
"function redeclaration differs because no return type is provided");
auto diag =
new_return_type_id.has_value()
? context.emitter().Build(new_function.latest_decl_id(),
FunctionRedeclReturnTypeDiffers,
new_return_type_id)
: context.emitter().Build(new_function.latest_decl_id(),
FunctionRedeclReturnTypeDiffersNoReturn);
if (prev_return_type_id.has_value()) {
CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePrevious, Note,
"previously declared with return type {0}",
SemIR::TypeId);
diag.Note(prev_function.latest_decl_id(),
FunctionRedeclReturnTypePrevious, prev_return_type_id);
} else {
CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePreviousNoReturn, Note,
"previously declared with no return type");
diag.Note(prev_function.latest_decl_id(),
FunctionRedeclReturnTypePreviousNoReturn);
}
diag.Emit();
return false;
}
return true;
}
auto CheckFunctionReturnType(Context& context, SemIR::LocId loc_id,
const SemIR::Function& function,
SemIR::SpecificId specific_id)
-> SemIR::ReturnTypeInfo {
auto return_info = SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(),
function, specific_id);
// If we couldn't determine the return information due to the return type
// being incomplete, try to complete it now.
if (return_info.init_repr.kind == SemIR::InitRepr::Incomplete) {
auto diagnose_incomplete_return_type = [&] {
CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error,
"function returns incomplete type {0}", SemIR::TypeId);
return context.emitter().Build(loc_id, IncompleteTypeInFunctionReturnType,
return_info.type_id);
};
auto diagnose_abstract_return_type = [&] {
CARBON_DIAGNOSTIC(AbstractTypeInFunctionReturnType, Error,
"function returns abstract type {0}", SemIR::TypeId);
return context.emitter().Build(loc_id, AbstractTypeInFunctionReturnType,
return_info.type_id);
};
// TODO: Consider suppressing the diagnostic if we've already diagnosed a
// definition or call to this function.
if (RequireConcreteType(context, return_info.type_id, loc_id,
diagnose_incomplete_return_type,
diagnose_abstract_return_type)) {
return_info = SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(),
function, specific_id);
}
}
return return_info;
}
} // namespace Carbon::Check