P0962R1: only use the member form of 'begin' and 'end' in a range-based
for loop if both members exist.
This resolves a DR whereby an errant 'begin' or 'end' member in a base
class could result in a derived class not being usable as a range with
non-member 'begin' and 'end'.
llvm-svn: 342925
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 61c6b37..ed2fe4e 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2149,6 +2149,56 @@
Sema::LookupMemberName);
LookupResult EndMemberLookup(SemaRef, EndNameInfo, Sema::LookupMemberName);
+ auto BuildBegin = [&] {
+ *BEF = BEF_begin;
+ Sema::ForRangeStatus RangeStatus =
+ SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, BeginNameInfo,
+ BeginMemberLookup, CandidateSet,
+ BeginRange, BeginExpr);
+
+ if (RangeStatus != Sema::FRS_Success) {
+ if (RangeStatus == Sema::FRS_DiagnosticIssued)
+ SemaRef.Diag(BeginRange->getBeginLoc(), diag::note_in_for_range)
+ << ColonLoc << BEF_begin << BeginRange->getType();
+ return RangeStatus;
+ }
+ if (!CoawaitLoc.isInvalid()) {
+ // FIXME: getCurScope() should not be used during template instantiation.
+ // We should pick up the set of unqualified lookup results for operator
+ // co_await during the initial parse.
+ *BeginExpr = SemaRef.ActOnCoawaitExpr(SemaRef.getCurScope(), ColonLoc,
+ BeginExpr->get());
+ if (BeginExpr->isInvalid())
+ return Sema::FRS_DiagnosticIssued;
+ }
+ if (FinishForRangeVarDecl(SemaRef, BeginVar, BeginExpr->get(), ColonLoc,
+ diag::err_for_range_iter_deduction_failure)) {
+ NoteForRangeBeginEndFunction(SemaRef, BeginExpr->get(), *BEF);
+ return Sema::FRS_DiagnosticIssued;
+ }
+ return Sema::FRS_Success;
+ };
+
+ auto BuildEnd = [&] {
+ *BEF = BEF_end;
+ Sema::ForRangeStatus RangeStatus =
+ SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, EndNameInfo,
+ EndMemberLookup, CandidateSet,
+ EndRange, EndExpr);
+ if (RangeStatus != Sema::FRS_Success) {
+ if (RangeStatus == Sema::FRS_DiagnosticIssued)
+ SemaRef.Diag(EndRange->getBeginLoc(), diag::note_in_for_range)
+ << ColonLoc << BEF_end << EndRange->getType();
+ return RangeStatus;
+ }
+ if (FinishForRangeVarDecl(SemaRef, EndVar, EndExpr->get(), ColonLoc,
+ diag::err_for_range_iter_deduction_failure)) {
+ NoteForRangeBeginEndFunction(SemaRef, EndExpr->get(), *BEF);
+ return Sema::FRS_DiagnosticIssued;
+ }
+ return Sema::FRS_Success;
+ };
+
if (CXXRecordDecl *D = RangeType->getAsCXXRecordDecl()) {
// - if _RangeT is a class type, the unqualified-ids begin and end are
// looked up in the scope of class _RangeT as if by class member access
@@ -2156,68 +2206,62 @@
// declaration, begin-expr and end-expr are __range.begin() and
// __range.end(), respectively;
SemaRef.LookupQualifiedName(BeginMemberLookup, D);
+ if (BeginMemberLookup.isAmbiguous())
+ return Sema::FRS_DiagnosticIssued;
+
SemaRef.LookupQualifiedName(EndMemberLookup, D);
+ if (EndMemberLookup.isAmbiguous())
+ return Sema::FRS_DiagnosticIssued;
if (BeginMemberLookup.empty() != EndMemberLookup.empty()) {
- SourceLocation RangeLoc = BeginVar->getLocation();
- *BEF = BeginMemberLookup.empty() ? BEF_end : BEF_begin;
+ // Look up the non-member form of the member we didn't find, first.
+ // This way we prefer a "no viable 'end'" diagnostic over a "i found
+ // a 'begin' but ignored it because there was no member 'end'"
+ // diagnostic.
+ auto BuildNonmember = [&](
+ BeginEndFunction BEFFound, LookupResult &Found,
+ llvm::function_ref<Sema::ForRangeStatus()> BuildFound,
+ llvm::function_ref<Sema::ForRangeStatus()> BuildNotFound) {
+ LookupResult OldFound = std::move(Found);
+ Found.clear();
- SemaRef.Diag(RangeLoc, diag::err_for_range_member_begin_end_mismatch)
- << RangeLoc << BeginRange->getType() << *BEF;
- return Sema::FRS_DiagnosticIssued;
+ if (Sema::ForRangeStatus Result = BuildNotFound())
+ return Result;
+
+ switch (BuildFound()) {
+ case Sema::FRS_Success:
+ return Sema::FRS_Success;
+
+ case Sema::FRS_NoViableFunction:
+ SemaRef.Diag(BeginRange->getBeginLoc(), diag::err_for_range_invalid)
+ << BeginRange->getType() << BEFFound;
+ CandidateSet->NoteCandidates(SemaRef, OCD_AllCandidates, BeginRange);
+ LLVM_FALLTHROUGH;
+
+ case Sema::FRS_DiagnosticIssued:
+ for (NamedDecl *D : OldFound) {
+ SemaRef.Diag(D->getLocation(),
+ diag::note_for_range_member_begin_end_ignored)
+ << BeginRange->getType() << BEFFound;
+ }
+ return Sema::FRS_DiagnosticIssued;
+ }
+ llvm_unreachable("unexpected ForRangeStatus");
+ };
+ if (BeginMemberLookup.empty())
+ return BuildNonmember(BEF_end, EndMemberLookup, BuildEnd, BuildBegin);
+ return BuildNonmember(BEF_begin, BeginMemberLookup, BuildBegin, BuildEnd);
}
} else {
// - otherwise, begin-expr and end-expr are begin(__range) and
// end(__range), respectively, where begin and end are looked up with
// argument-dependent lookup (3.4.2). For the purposes of this name
// lookup, namespace std is an associated namespace.
-
}
- *BEF = BEF_begin;
- Sema::ForRangeStatus RangeStatus =
- SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, BeginNameInfo,
- BeginMemberLookup, CandidateSet,
- BeginRange, BeginExpr);
-
- if (RangeStatus != Sema::FRS_Success) {
- if (RangeStatus == Sema::FRS_DiagnosticIssued)
- SemaRef.Diag(BeginRange->getBeginLoc(), diag::note_in_for_range)
- << ColonLoc << BEF_begin << BeginRange->getType();
- return RangeStatus;
- }
- if (!CoawaitLoc.isInvalid()) {
- // FIXME: getCurScope() should not be used during template instantiation.
- // We should pick up the set of unqualified lookup results for operator
- // co_await during the initial parse.
- *BeginExpr = SemaRef.ActOnCoawaitExpr(SemaRef.getCurScope(), ColonLoc,
- BeginExpr->get());
- if (BeginExpr->isInvalid())
- return Sema::FRS_DiagnosticIssued;
- }
- if (FinishForRangeVarDecl(SemaRef, BeginVar, BeginExpr->get(), ColonLoc,
- diag::err_for_range_iter_deduction_failure)) {
- NoteForRangeBeginEndFunction(SemaRef, BeginExpr->get(), *BEF);
- return Sema::FRS_DiagnosticIssued;
- }
-
- *BEF = BEF_end;
- RangeStatus =
- SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, EndNameInfo,
- EndMemberLookup, CandidateSet,
- EndRange, EndExpr);
- if (RangeStatus != Sema::FRS_Success) {
- if (RangeStatus == Sema::FRS_DiagnosticIssued)
- SemaRef.Diag(EndRange->getBeginLoc(), diag::note_in_for_range)
- << ColonLoc << BEF_end << EndRange->getType();
- return RangeStatus;
- }
- if (FinishForRangeVarDecl(SemaRef, EndVar, EndExpr->get(), ColonLoc,
- diag::err_for_range_iter_deduction_failure)) {
- NoteForRangeBeginEndFunction(SemaRef, EndExpr->get(), *BEF);
- return Sema::FRS_DiagnosticIssued;
- }
- return Sema::FRS_Success;
+ if (Sema::ForRangeStatus Result = BuildBegin())
+ return Result;
+ return BuildEnd();
}
/// Speculatively attempt to dereference an invalid range expression.