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:
Christopher Di Bella
2026-05-22 21:12:06 +00:00
committed by GitHub
parent e6b1679552
commit 64e890c246
5 changed files with 1466 additions and 153 deletions
+71 -21
View File
@@ -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,
+3 -4
View File
@@ -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
+2 -2
View File
@@ -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
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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);
}