diff options
| author | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-09-24 23:17:44 +0000 |
|---|---|---|
| committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-09-24 23:17:44 +0000 |
| commit | 236ffdeeb2e013c8368f1cf0904d1d43f30f4d2d (patch) | |
| tree | 6d75afeb261023a5085de900ca02c7e3a4bf5d8a /clang/lib | |
| parent | 44ecb0e3c20153860b0f303e1ab8a29d11ae12ba (diff) | |
| download | bcm5719-llvm-236ffdeeb2e013c8368f1cf0904d1d43f30f4d2d.tar.gz bcm5719-llvm-236ffdeeb2e013c8368f1cf0904d1d43f30f4d2d.zip | |
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
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Sema/SemaStmt.cpp | 146 |
1 files changed, 95 insertions, 51 deletions
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 61c6b37b4c7..ed2fe4e3af8 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2149,6 +2149,56 @@ BuildNonArrayForRange(Sema &SemaRef, Expr *BeginRange, Expr *EndRange, 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 @@ BuildNonArrayForRange(Sema &SemaRef, Expr *BeginRange, Expr *EndRange, // 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; - - SemaRef.Diag(RangeLoc, diag::err_for_range_member_begin_end_mismatch) - << RangeLoc << BeginRange->getType() << *BEF; - return Sema::FRS_DiagnosticIssued; + // 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(); + + 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. |

