Canonicalize generated functions for Core witnesses (#7729)

Use a single `SemIR::Function` per `Core` interface method, whether it's
generated locally or imported. This prevents generating duplicate
functions, which lead to different types when the witness appears in a
`FacetValue` as part of a specific for a class.

We use a `CanonicalValueStore` of `GeneratedFunction` objects that allow
finding an existing FunctionId for a `Generated` special function before
(re-)generating it. Mangling for `Generated` functions is also moved to
use the values from the `GeneratedFunction`'s canonicalization key, so
that we have a consistent source of truth for the unique ID of a
`Generated` function across all files.

New tests are in
`toolchain/check/testdata/impl/custom_witness/destroy.carbon`.
This commit is contained in:
Dana Jansens
2026-09-15 16:02:13 +00:00
committed by GitHub
parent db2e26ba86
commit 4416f3525b
278 changed files with 5941 additions and 5506 deletions
+51 -3
View File
@@ -7,6 +7,7 @@
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/Sema.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/core_identifier.h"
#include "toolchain/check/cpp/import.h"
@@ -16,6 +17,7 @@
#include "toolchain/check/custom_witness.h"
#include "toolchain/check/function.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/pattern.h"
#include "toolchain/check/type.h"
#include "toolchain/check/type_completion.h"
@@ -491,6 +493,13 @@ namespace {
struct OverloadedOperatorInfo {
enum ReturnType { FirstArgType, Bool };
// The name of the interface containing the operator function. This affects
// the mangled name and canonicalization of Generated functions.
//
// This must always be set, so we pick a default value that does not represent
// an interface, so is never correct.
CoreIdentifier interface_name = CoreIdentifier::VoidBase;
// The name for the function used to implement this operator. This is usually
// `Op`. This mostly only affects the mangled name, but might show up in
// diagnostics.
@@ -517,49 +526,83 @@ static auto GetBuiltinOperatorInfo(clang::OverloadedOperatorKind kind)
// Bitwise operators. In C++, the return type is computed with the usual
// arithmetic conversions, but we will just use the type of the arguments.
table[clang::OO_Amp] = {
.interface_name = CoreIdentifier::BitAndWith,
.builtin_kind = SemIR::BuiltinFunctionKind::IntAnd,
.return_type = OverloadedOperatorInfo::ReturnType::FirstArgType};
table[clang::OO_Pipe] = {
.interface_name = CoreIdentifier::BitOrWith,
.builtin_kind = SemIR::BuiltinFunctionKind::IntOr,
.return_type = OverloadedOperatorInfo::ReturnType::FirstArgType};
table[clang::OO_Caret] = {
.interface_name = CoreIdentifier::BitXorWith,
.builtin_kind = SemIR::BuiltinFunctionKind::IntXor,
.return_type = OverloadedOperatorInfo::ReturnType::FirstArgType};
table[clang::OO_Tilde] = {
.interface_name = CoreIdentifier::BitComplement,
.builtin_kind = SemIR::BuiltinFunctionKind::IntComplement,
.return_type = OverloadedOperatorInfo::ReturnType::FirstArgType};
// Comparison operators.
table[clang::OO_EqualEqual] = {
.interface_name = CoreIdentifier::OrderedWith,
.op_name = CoreIdentifier::Equal,
.builtin_kind = SemIR::BuiltinFunctionKind::IntEq,
.return_type = OverloadedOperatorInfo::ReturnType::Bool};
table[clang::OO_ExclaimEqual] = {
.interface_name = CoreIdentifier::OrderedWith,
.op_name = CoreIdentifier::NotEqual,
.builtin_kind = SemIR::BuiltinFunctionKind::IntNeq,
.return_type = OverloadedOperatorInfo::ReturnType::Bool};
table[clang::OO_Less] = {
.interface_name = CoreIdentifier::OrderedWith,
.op_name = CoreIdentifier::Less,
.builtin_kind = SemIR::BuiltinFunctionKind::IntLess,
.return_type = OverloadedOperatorInfo::ReturnType::Bool};
table[clang::OO_LessEqual] = {
.interface_name = CoreIdentifier::OrderedWith,
.op_name = CoreIdentifier::LessOrEquivalent,
.builtin_kind = SemIR::BuiltinFunctionKind::IntLessEq,
.return_type = OverloadedOperatorInfo::ReturnType::Bool};
table[clang::OO_Greater] = {
.interface_name = CoreIdentifier::OrderedWith,
.op_name = CoreIdentifier::Greater,
.builtin_kind = SemIR::BuiltinFunctionKind::IntGreater,
.return_type = OverloadedOperatorInfo::ReturnType::Bool};
table[clang::OO_GreaterEqual] = {
.interface_name = CoreIdentifier::OrderedWith,
.op_name = CoreIdentifier::GreaterOrEquivalent,
.builtin_kind = SemIR::BuiltinFunctionKind::IntGreaterEq,
.return_type = OverloadedOperatorInfo::ReturnType::Bool};
return table;
}();
return OpTable[kind];
}
static auto GetCoreInterfaceId(Context& context, SemIR::LocId loc_id,
CoreIdentifier interface_name)
-> SemIR::InterfaceId {
auto inst_id = LookupNameInCore(context, loc_id, interface_name);
// Non-generic interfaces.
if (auto facet_type = context.insts().TryGetAs<SemIR::FacetType>(inst_id)) {
const auto& declared =
context.declared_facet_types().Get(facet_type->declared_facet_type_id);
auto single = declared.TryAsSingleExtend();
CARBON_KIND_SWITCH(*single) {
case CARBON_KIND(SemIR::SpecificInterface si): {
return si.interface_id;
}
case CARBON_KIND(SemIR::SpecificNamedConstraint _): {
CARBON_FATAL("Operators in named constraints are not yet needed");
}
}
}
auto type_id = context.insts().Get(inst_id).type_id();
auto generic = context.types().GetAs<SemIR::GenericInterfaceType>(type_id);
return generic.interface_id;
}
// Builds a Carbon builtin function declaration corresponding to an overload
// candidate that selected a C++ builtin operator. Returns None if no
// corresponding builtin function could or should be built.
@@ -572,6 +615,9 @@ static auto TryBuildBuiltinOperator(
return SemIR::InstId::None;
}
CARBON_CHECK(info.interface_name != CoreIdentifier::VoidBase,
"builtin operator interface was not specified");
// Import the argument types. For now, we only accept enum types.
// TODO: Consider expanding this to other types.
llvm::SmallVector<SemIR::TypeId, 2> arg_type_ids;
@@ -617,8 +663,10 @@ static auto TryBuildBuiltinOperator(
break;
}
return MakeBuiltinOperatorFunction(context, arg_type_ids, return_type_id,
info.op_name, info.builtin_kind);
return MakeBuiltinOperatorFunction(
context, loc_id, arg_type_ids, return_type_id, info.op_name,
info.builtin_kind,
GetCoreInterfaceId(context, loc_id, info.interface_name));
}
namespace {