// 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 template_arg_ids, clang::Expr* self_arg, llvm::ArrayRef 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(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(decl); auto* method_decl = dyn_cast(fn_decl); if (method_decl && !method_decl->isStatic() && !isa(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(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 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, const SemIR::CppOverloadSet& overload_set, llvm::ArrayRef template_arg_ids, SemIR::InstId self_id, llvm::ArrayRef 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; 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(arg_exprs.size())}); if (result_id != SemIR::ErrorInst::InstId) { CheckCppOverloadAccess( context, loc_id, best_viable_fn->FoundDecl, context.insts().GetAsKnownInstId(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