Support detecting begin()/end() methods for range-for loops (#7185)

This commit adds support for range-based for loops using C++ types. It's
currently limited to detecting that `r.begin()` and `r.end()` are
available. We should be able to add full support for methods after #7181
is merged.

Support for ADL is still a work-in-progress, and will be added at a
later time.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Christopher Di Bella
2026-05-12 19:03:55 +00:00
committed by GitHub
co-authored by Richard Smith
parent dd7cfdb149
commit 2aaa061688
6 changed files with 487 additions and 0 deletions
+119
View File
@@ -4,6 +4,8 @@
#include "toolchain/check/cpp/impl_lookup.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/core_identifier.h"
@@ -11,6 +13,7 @@
#include "toolchain/check/cpp/location.h"
#include "toolchain/check/cpp/operators.h"
#include "toolchain/check/cpp/overload_resolution.h"
#include "toolchain/check/cpp/type_mapping.h"
#include "toolchain/check/custom_witness.h"
#include "toolchain/check/impl.h"
#include "toolchain/check/impl_lookup.h"
@@ -353,6 +356,118 @@ 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 {
constexpr auto LookupKind = clang::Sema::LookupMemberName;
auto lookup_info = clang::LookupResult(clang_sema, name_info, LookupKind);
clang_sema.LookupQualifiedName(lookup_info, class_decl);
if (lookup_info.empty()) {
return SemIR::InstId::None;
}
if (!lookup_info.isSingleResult()) {
context.TODO(loc_id, "{method_name} overload sets unsupported");
return SemIR::ErrorInst::InstId;
}
auto decl_info = DeclInfo{
.decl = *lookup_info.begin(),
.signature = {.num_params = 0},
};
return GetFunctionId(context, loc_id, decl_info);
}
static auto LookupCppUnqualified(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 {
(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;
}
using LookupBeginEndCallees = auto(Context&, clang::Sema&, SemIR::LocId,
const clang::DeclarationNameInfo&,
clang::CXXRecordDecl*, SemIR::ConstantId)
-> SemIR::InstId;
static auto BuildCppRangeForIterateWitnessImpl(
Context& context, SemIR::LocId loc_id,
LookupBeginEndCallees range_for_lookup, clang::CXXRecordDecl* class_decl,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId {
auto& clang_sema = context.clang_sema();
auto begin_name_info = clang::DeclarationNameInfo(
&clang_sema.PP.getIdentifierTable().get("begin"),
clang::SourceLocation());
auto begin_fn_id =
range_for_lookup(context, clang_sema, loc_id, begin_name_info, class_decl,
query_self_const_id);
if (begin_fn_id == SemIR::InstId::None ||
begin_fn_id == SemIR::ErrorInst::InstId) {
return begin_fn_id;
}
auto begin_result_type_id =
context.functions()
.Get(context.insts()
.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;
}
auto end_name_info = clang::DeclarationNameInfo(
&clang_sema.PP.getIdentifierTable().get("end"), clang::SourceLocation());
auto end_fn_id = range_for_lookup(context, clang_sema, loc_id, end_name_info,
class_decl, query_self_const_id);
if (end_fn_id == SemIR::InstId::None ||
end_fn_id == SemIR::ErrorInst::InstId) {
return end_fn_id;
}
auto end_result_type_id =
context.functions()
.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;
}
return BuildCustomWitness(
context, loc_id, query_self_const_id, query_specific_interface_id,
{begin_result_type_id, end_result_type_id, begin_fn_id, end_fn_id});
}
static auto BuildCppRangeForIterateWitness(
Context& context, SemIR::LocId loc_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId {
auto* class_decl = TypeAsClassDecl(context, query_self_const_id);
if (auto with_members = BuildCppRangeForIterateWitnessImpl(
context, loc_id, LookupCppMethod, class_decl, query_self_const_id,
query_specific_interface_id);
with_members != SemIR::InstId::None) {
return with_members;
}
return BuildCppRangeForIterateWitnessImpl(
context, loc_id, LookupCppUnqualified, class_decl, query_self_const_id,
query_specific_interface_id);
}
auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
SemIR::CoreInterface core_interface,
SemIR::ConstantId query_self_const_id,
@@ -417,6 +532,10 @@ auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
return BuildDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface_id);
case SemIR::CoreInterface::CppRangeForIterate:
return BuildCppRangeForIterateWitness(
context, loc_id, query_self_const_id, query_specific_interface_id);
// IntFitsIn is for Carbon integer types only.
case SemIR::CoreInterface::IntFitsIn:
return SemIR::InstId::None;