mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 13:50:10 +01:00
Extend CppRangeForIterate to support ADL begin/end (#7230)
`CppRangeForIterate` needs to support finding `begin()`/`end()` as a pair of methods and as a pair of ADL-findable functions. This change adds the ADL component, which lets us also diagnose types that don't implement the interface. Unlike methods, we apparently have support for overloads when using ADL.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
#include "toolchain/sem_ir/builtin_function_kind.h"
|
||||
#include "toolchain/sem_ir/clang_decl.h"
|
||||
#include "toolchain/sem_ir/core_interface.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
@@ -381,11 +382,12 @@ static auto BuildCppComparisonWitness(
|
||||
query_specific_interface_id, operators);
|
||||
}
|
||||
|
||||
static auto LookupCppMethod(
|
||||
Context& context, clang::Sema& clang_sema, SemIR::LocId loc_id,
|
||||
const clang::DeclarationNameInfo& name_info,
|
||||
clang::CXXRecordDecl* class_decl,
|
||||
[[maybe_unused]] SemIR::ConstantId query_self_const_id) -> SemIR::InstId {
|
||||
static auto LookupCppMethod(Context& context, clang::Sema& clang_sema,
|
||||
SemIR::LocId loc_id,
|
||||
const clang::DeclarationNameInfo& name_info,
|
||||
clang::CXXRecordDecl* class_decl,
|
||||
SemIR::ConstantId /*query_self_const_id*/)
|
||||
-> SemIR::InstId {
|
||||
constexpr auto LookupKind = clang::Sema::LookupMemberName;
|
||||
auto lookup_info = clang::LookupResult(clang_sema, name_info, LookupKind);
|
||||
clang_sema.LookupQualifiedName(lookup_info, class_decl);
|
||||
@@ -394,10 +396,14 @@ static auto LookupCppMethod(
|
||||
}
|
||||
|
||||
if (!lookup_info.isSingleResult()) {
|
||||
context.TODO(loc_id, "{method_name} overload sets unsupported");
|
||||
context.TODO(loc_id, "overload sets unsupported");
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
if (lookup_info.begin()->getKind() != clang::Decl::CXXMethod) {
|
||||
return SemIR::InstId::None;
|
||||
}
|
||||
|
||||
auto decl_info = DeclInfo{
|
||||
.decl = *lookup_info.begin(),
|
||||
.signature_id = MakeSignature(
|
||||
@@ -408,15 +414,63 @@ static auto LookupCppMethod(
|
||||
static auto LookupCppUnqualified(Context& context, clang::Sema& clang_sema,
|
||||
SemIR::LocId loc_id,
|
||||
const clang::DeclarationNameInfo& name_info,
|
||||
clang::CXXRecordDecl* class_decl,
|
||||
clang::CXXRecordDecl* /*class_decl*/,
|
||||
SemIR::ConstantId query_self_const_id)
|
||||
-> SemIR::InstId {
|
||||
(void)clang_sema;
|
||||
(void)name_info;
|
||||
(void)class_decl;
|
||||
(void)query_self_const_id;
|
||||
context.TODO(loc_id, "support ADL begin/end");
|
||||
return SemIR::ErrorInst::InstId;
|
||||
auto lookup_result = clang_sema.CreateUnresolvedLookupExpr(
|
||||
/*NamingClass=*/nullptr, clang::NestedNameSpecifierLoc(), name_info,
|
||||
clang::UnresolvedSet<0>());
|
||||
if (lookup_result.isInvalid()) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
auto* function = llvm::cast<clang::UnresolvedLookupExpr>(lookup_result.get());
|
||||
|
||||
auto candidate_set = clang::OverloadCandidateSet(
|
||||
clang::SourceLocation(),
|
||||
clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal);
|
||||
|
||||
auto self_type_id =
|
||||
context.types().GetTypeIdForTypeConstantId(query_self_const_id);
|
||||
auto type = MapToCppType(context, self_type_id);
|
||||
if (type.isNull()) {
|
||||
return SemIR::InstId::None;
|
||||
}
|
||||
|
||||
auto synthesised_expr =
|
||||
clang::OpaqueValueExpr({}, type, clang::ExprValueKind::VK_LValue);
|
||||
auto* args = static_cast<clang::Expr*>(&synthesised_expr);
|
||||
clang_sema.AddArgumentDependentLookupCandidates(name_info.getName(), {}, args,
|
||||
{}, candidate_set);
|
||||
|
||||
if (candidate_set.empty()) {
|
||||
return SemIR::InstId::None;
|
||||
}
|
||||
|
||||
auto* best_candidate = clang::OverloadCandidateSet::iterator();
|
||||
auto overload_result =
|
||||
candidate_set.BestViableFunction(clang_sema, {}, best_candidate);
|
||||
switch (overload_result) {
|
||||
case clang::OR_No_Viable_Function:
|
||||
case clang::OR_Ambiguous:
|
||||
return SemIR::InstId::None;
|
||||
case clang::OR_Deleted: {
|
||||
auto loc = GetCppLocation(context, loc_id);
|
||||
clang_sema.DiagnoseUseOfDeletedFunction(
|
||||
loc, clang::SourceRange(loc, loc), name_info.getName(), candidate_set,
|
||||
best_candidate->Function, args);
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
case clang::OR_Success: {
|
||||
using enum SemIR::ClangDeclSignature::PassingMode;
|
||||
auto decl_info = DeclInfo{
|
||||
.decl = best_candidate->Function,
|
||||
.signature_id = ComputeClangDeclSignatureFromBestViableFunction(
|
||||
context, best_candidate, function, args),
|
||||
};
|
||||
return GetFunctionId(context, loc_id, decl_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using LookupBeginEndCallees = auto(Context&, clang::Sema&, SemIR::LocId,
|
||||
@@ -447,10 +501,8 @@ static auto BuildCppRangeForIterateWitnessImpl(
|
||||
.GetAs<SemIR::FunctionDecl>(begin_fn_id)
|
||||
.function_id)
|
||||
.return_type_inst_id;
|
||||
if (begin_result_type_id == SemIR::ErrorInst::InstId ||
|
||||
begin_result_type_id == SemIR::InstId::None) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
CARBON_CHECK(begin_result_type_id != SemIR::ErrorInst::InstId &&
|
||||
begin_result_type_id != SemIR::InstId::None);
|
||||
|
||||
auto end_name_info = clang::DeclarationNameInfo(
|
||||
&clang_sema.PP.getIdentifierTable().get("end"), clang::SourceLocation());
|
||||
@@ -466,10 +518,8 @@ static auto BuildCppRangeForIterateWitnessImpl(
|
||||
.Get(
|
||||
context.insts().GetAs<SemIR::FunctionDecl>(end_fn_id).function_id)
|
||||
.return_type_inst_id;
|
||||
if (end_result_type_id == SemIR::ErrorInst::InstId ||
|
||||
end_result_type_id == SemIR::InstId::None) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
CARBON_CHECK(end_result_type_id != SemIR::ErrorInst::InstId &&
|
||||
end_result_type_id != SemIR::InstId::None);
|
||||
|
||||
return BuildCustomWitness(
|
||||
context, loc_id, query_self_const_id, query_specific_interface_id,
|
||||
|
||||
@@ -745,8 +745,8 @@ auto IsCppOperatorMethod(Context& context, SemIR::InstId inst_id) -> bool {
|
||||
return function_decl && IsCppOperatorMethodDecl(function_decl);
|
||||
}
|
||||
|
||||
auto IsCppConstructorOrNonMethodOperator(Context& context,
|
||||
SemIR::InstId inst_id) -> bool {
|
||||
auto IsCppConstructorOrNonMethod(Context& context, SemIR::InstId inst_id)
|
||||
-> bool {
|
||||
auto* function_decl = GetAsCppFunctionDecl(context, inst_id);
|
||||
if (!function_decl) {
|
||||
return false;
|
||||
@@ -754,8 +754,7 @@ auto IsCppConstructorOrNonMethodOperator(Context& context,
|
||||
if (isa<clang::CXXConstructorDecl>(function_decl)) {
|
||||
return true;
|
||||
}
|
||||
return !isa<clang::CXXMethodDecl>(function_decl) &&
|
||||
function_decl->isOverloadedOperator();
|
||||
return !isa<clang::CXXMethodDecl>(function_decl);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -38,8 +38,8 @@ auto IsCppOperatorMethod(Context& context, SemIR::InstId inst_id) -> bool;
|
||||
// Returns whether the specified instruction refers to a C++ constructor or
|
||||
// non-operator method. If so, when mapping from a Carbon interface to a C++
|
||||
// call, we pass a `self` parameter as the first argument instead.
|
||||
auto IsCppConstructorOrNonMethodOperator(Context& context,
|
||||
SemIR::InstId inst_id) -> bool;
|
||||
auto IsCppConstructorOrNonMethod(Context& context, SemIR::InstId inst_id)
|
||||
-> bool;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
|
||||
+1389
-125
File diff suppressed because it is too large
Load Diff
@@ -306,7 +306,7 @@ auto PerformThunkCall(Context& context, SemIR::LocId loc_id,
|
||||
// `self` parameter but C++ models that parameter as an explicit argument
|
||||
// instead, so add the `self` to the argument list instead in that case.
|
||||
if (function.self_param_id.has_value() &&
|
||||
!IsCppConstructorOrNonMethodOperator(context, callee_id)) {
|
||||
!IsCppConstructorOrNonMethod(context, callee_id)) {
|
||||
callee_id = PerformCompoundMemberAccess(context, loc_id,
|
||||
args.consume_front(), callee_id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user