mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-02 22:02:51 +01:00
Treat the initial sequence ofarguments in a call to a C++ function up to and including the last argument that is a type or template as being the explicit template arguments for the call, rather than rejecting them because they can't be converted to the parameter types. Implements the current direction on leads issue #6768, except that no syntax for explicitly annotating an argument as being a template argument is provided. --------- Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
223 lines
9.2 KiB
C++
223 lines
9.2 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/call.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/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(
|
|
Context& context, clang::OverloadCandidateSet& candidate_set,
|
|
const clang::UnresolvedSet<4>& functions,
|
|
llvm::ArrayRef<SemIR::InstId> template_arg_ids, clang::Expr* self_arg,
|
|
llvm::ArrayRef<clang::Expr*> args) -> void {
|
|
clang::Sema& sema = context.clang_sema();
|
|
|
|
constexpr bool SuppressUserConversions = false;
|
|
constexpr bool PartialOverloading = false;
|
|
|
|
for (auto found_decl : functions.pairs()) {
|
|
auto* decl = found_decl->getUnderlyingDecl();
|
|
|
|
// Form an explicit template argument list if needed. Note that this is done
|
|
// per-candidate, as the conversions performed on the template arguments
|
|
// differ based on the corresponding template parameters.
|
|
auto* template_decl = dyn_cast<clang::FunctionTemplateDecl>(decl);
|
|
clang::TemplateArgumentListInfo explicit_template_arg_storage;
|
|
clang::TemplateArgumentListInfo* explicit_template_args = nullptr;
|
|
if (!template_arg_ids.empty()) {
|
|
if (!template_decl) {
|
|
continue;
|
|
}
|
|
if (!ConvertArgsToTemplateArgs(context, template_decl, template_arg_ids,
|
|
explicit_template_arg_storage,
|
|
/*diagnose=*/false)) {
|
|
continue;
|
|
}
|
|
explicit_template_args = &explicit_template_arg_storage;
|
|
}
|
|
|
|
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()),
|
|
explicit_template_args, 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, explicit_template_args, 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,
|
|
llvm::ArrayRef<SemIR::InstId> template_arg_ids, 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);
|
|
|
|
AddOverloadCandidates(context, candidate_set,
|
|
overload_set.candidate_functions, template_arg_ids,
|
|
self_expr, arg_exprs);
|
|
|
|
// Find best viable function among the candidates.
|
|
clang::Sema& sema = context.clang_sema();
|
|
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,
|
|
{.num_params = static_cast<int32_t>(arg_exprs.size())});
|
|
if (result_id != SemIR::ErrorInst::InstId) {
|
|
CheckCppOverloadAccess(
|
|
context, loc_id, best_viable_fn->FoundDecl,
|
|
context.insts().GetAsKnownInstId<SemIR::FunctionDecl>(result_id),
|
|
overload_set.parent_scope_id);
|
|
}
|
|
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
|