Files
carbon-lang/toolchain/check/cpp/overload_resolution.cpp
T
372f632d9d Implement support for copying C++ classes. (#6434)
When performing impl lookup for `Core.Copy` for a C++ class type, look
for a copy constructor. If we find one, synthesize an impl witness that
calls the constructor.

This adds initial support for impl lookup to delegate to the C++ interop
logic for queries involving C++ types. For now, we don't implement the
rules from #6166 that compare a synthesized type structure for the C++
impl against the best Carbon type structure, but the framework for
building that support is established here.

Currently there is no caching of the lookup here, and we build unique
`ImplWitnessTable`s for each lookup, which leads to each impl lookup
producing a distinct facet value. This results in some errors in generic
contexts; this will be addressed in follow-up changes. This PR aims only
to support the non-generic case.

---------

Co-authored-by: Dana Jansens <danakj@orodu.net>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
2025-12-02 02:52:23 +00:00

203 lines
8.5 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/cpp/overload_resolution.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/Sema.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/cpp/access.h"
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/cpp/location.h"
#include "toolchain/check/cpp/operators.h"
#include "toolchain/check/cpp/type_mapping.h"
#include "toolchain/check/member_access.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/name_scope.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
// Map a Carbon name into a C++ name.
static auto GetCppName(Context& context, SemIR::NameId name_id)
-> clang::DeclarationName {
// TODO: Some special names should probably use different formatting. In
// particular, NameId::CppOperator should probably map back to a
// CXXOperatorName.
auto name_str = context.names().GetFormatted(name_id);
return clang::DeclarationName(&context.ast_context().Idents.get(name_str));
}
// Adds the given overload candidates to the candidate set.
static auto AddOverloadCandidates(clang::Sema& sema,
clang::OverloadCandidateSet& candidate_set,
const clang::UnresolvedSet<4>& functions,
clang::Expr* self_arg,
llvm::ArrayRef<clang::Expr*> args) -> void {
constexpr bool SuppressUserConversions = false;
constexpr bool PartialOverloading = false;
constexpr clang::TemplateArgumentListInfo* ExplicitTemplateArgs = nullptr;
for (auto found_decl : functions.pairs()) {
auto* decl = found_decl->getUnderlyingDecl();
auto* template_decl = dyn_cast<clang::FunctionTemplateDecl>(decl);
auto* fn_decl = template_decl ? template_decl->getTemplatedDecl()
: cast<clang::FunctionDecl>(decl);
auto* method_decl = dyn_cast<clang::CXXMethodDecl>(fn_decl);
if (method_decl && !method_decl->isStatic() &&
!isa<clang::CXXConstructorDecl>(fn_decl)) {
clang::QualType self_type;
clang::Expr::Classification self_classification;
if (self_arg) {
self_type = self_arg->getType();
self_classification = self_arg->Classify(sema.Context);
}
if (template_decl) {
sema.AddMethodTemplateCandidate(
template_decl, found_decl,
cast<clang::CXXRecordDecl>(template_decl->getDeclContext()),
ExplicitTemplateArgs, self_type, self_classification, args,
candidate_set, SuppressUserConversions, PartialOverloading);
} else if (method_decl->isOverloadedOperator()) {
sema.AddMemberOperatorCandidates(method_decl->getOverloadedOperator(),
candidate_set.getLocation(), args,
candidate_set);
} else {
sema.AddMethodCandidate(method_decl, found_decl,
method_decl->getParent(), self_type,
self_classification, args, candidate_set,
SuppressUserConversions, PartialOverloading);
}
} else if (template_decl) {
sema.AddTemplateOverloadCandidate(
template_decl, found_decl, ExplicitTemplateArgs, args, candidate_set,
SuppressUserConversions, PartialOverloading);
} else {
sema.AddOverloadCandidate(fn_decl, found_decl, args, candidate_set,
SuppressUserConversions, PartialOverloading);
}
}
}
auto CheckCppOverloadAccess(
Context& context, SemIR::LocId loc_id, clang::DeclAccessPair overload,
SemIR::KnownInstId<SemIR::FunctionDecl> overload_inst_id,
SemIR::NameScopeId parent_scope_id) -> void {
SemIR::AccessKind member_access_kind = MapCppAccess(overload);
if (member_access_kind == SemIR::AccessKind::Public) {
return;
}
auto function_id = context.insts().Get(overload_inst_id).function_id;
auto& function = context.functions().Get(function_id);
if (!parent_scope_id.has_value()) {
parent_scope_id = function.parent_scope_id;
}
auto name_scope_const_id = context.constant_values().Get(
context.name_scopes().Get(parent_scope_id).inst_id());
SemIR::AccessKind allowed_access_kind =
GetHighestAllowedAccess(context, loc_id, name_scope_const_id);
CheckAccess(context, loc_id, SemIR::LocId(overload_inst_id), function.name_id,
member_access_kind,
/*is_parent_access=*/false,
{.constant_id = name_scope_const_id,
.highest_allowed_access = allowed_access_kind});
}
auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
SemIR::CppOverloadSetId overload_set_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
-> SemIR::InstId {
// Register an annotation scope to flush any Clang diagnostics when we return.
// This is important to ensure that Clang diagnostics are properly interleaved
// with Carbon diagnostics.
Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
[](auto& /*builder*/) {});
// Map Carbon call argument types to C++ types.
clang::Expr* self_expr = nullptr;
if (self_id.has_value()) {
self_expr = InventClangArg(context, self_id);
if (!self_expr) {
return SemIR::ErrorInst::InstId;
}
}
auto maybe_arg_exprs = InventClangArgs(context, arg_ids);
if (!maybe_arg_exprs.has_value()) {
return SemIR::ErrorInst::InstId;
}
auto& arg_exprs = *maybe_arg_exprs;
const SemIR::CppOverloadSet& overload_set =
context.cpp_overload_sets().Get(overload_set_id);
clang::SourceLocation loc = GetCppLocation(context, loc_id);
// Add candidate functions from the name lookup.
clang::OverloadCandidateSet candidate_set(
loc,
overload_set.operator_rewrite_info.OriginalOperator
? clang::OverloadCandidateSet::CandidateSetKind::CSK_Operator
: clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal,
overload_set.operator_rewrite_info);
clang::Sema& sema = context.clang_sema();
AddOverloadCandidates(sema, candidate_set, overload_set.candidate_functions,
self_expr, arg_exprs);
// Find best viable function among the candidates.
clang::OverloadCandidateSet::iterator best_viable_fn;
clang::OverloadingResult overloading_result =
candidate_set.BestViableFunction(sema, loc, best_viable_fn);
switch (overloading_result) {
case clang::OverloadingResult::OR_Success: {
CARBON_CHECK(best_viable_fn->Function);
CARBON_CHECK(!best_viable_fn->RewriteKind);
SemIR::InstId result_id = ImportCppFunctionDecl(
context, loc_id, best_viable_fn->Function, arg_exprs.size());
if (auto fn_decl =
context.insts().TryGetAsWithId<SemIR::FunctionDecl>(result_id)) {
CheckCppOverloadAccess(context, loc_id, best_viable_fn->FoundDecl,
fn_decl->inst_id, overload_set.parent_scope_id);
} else {
CARBON_CHECK(result_id == SemIR::ErrorInst::InstId);
}
return result_id;
}
case clang::OverloadingResult::OR_No_Viable_Function: {
candidate_set.NoteCandidates(
clang::PartialDiagnosticAt(
loc, sema.PDiag(clang::diag::err_ovl_no_viable_function_in_call)
<< GetCppName(context, overload_set.name_id)),
sema, clang::OCD_AllCandidates, arg_exprs);
return SemIR::ErrorInst::InstId;
}
case clang::OverloadingResult::OR_Ambiguous: {
candidate_set.NoteCandidates(
clang::PartialDiagnosticAt(
loc, sema.PDiag(clang::diag::err_ovl_ambiguous_call)
<< GetCppName(context, overload_set.name_id)),
sema, clang::OCD_AmbiguousCandidates, arg_exprs);
return SemIR::ErrorInst::InstId;
}
case clang::OverloadingResult::OR_Deleted: {
sema.DiagnoseUseOfDeletedFunction(
loc, clang::SourceRange(loc, loc),
GetCppName(context, overload_set.name_id), candidate_set,
best_viable_fn->Function, arg_exprs);
return SemIR::ErrorInst::InstId;
}
}
}
} // namespace Carbon::Check