diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 065b5b1bed74..aaa022e4b3dd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1415,7 +1415,15 @@ def err_for_range_member_begin_end_mismatch : Error< "range type %0 has '%select{begin|end}1' member but no '%select{end|begin}1' member">; def err_for_range_begin_end_types_differ : Error< "'begin' and 'end' must return the same type (got %0 and %1)">; -def note_for_range_type : Note<"range has type %0">; +def note_in_for_range: Note< + "when looking up '%select{begin|end}0' function for range expression " + "of type %1">; +def err_for_range_invalid: Error< + "invalid range expression of type %0; no viable '%select{begin|end}1' " + "function available">; +def err_for_range_dereference : Error< + "invalid range expression of type %0; did you mean to dereference it " + "with '*'?">; def note_for_range_begin_end : Note< "selected '%select{begin|end}0' %select{function|template }1%2 with iterator type %3">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4be532c56f72..4e2a9714a263 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1924,6 +1924,30 @@ public: OverloadCandidateSet &CandidateSet, bool PartialOverloading = false); + // An enum used to represent the different possible results of building a + // range-based for loop. + enum ForRangeStatus { + FRS_Success, + FRS_NoViableFunction, + FRS_DiagnosticIssued + }; + + // An enum to represent whether something is dealing with a call to begin() + // or a call to end() in a range-based for loop. + enum BeginEndFunction { + BEF_begin, + BEF_end + }; + + ForRangeStatus BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc, + SourceLocation RangeLoc, + VarDecl *Decl, + BeginEndFunction BEF, + const DeclarationNameInfo &NameInfo, + LookupResult &MemberLookup, + OverloadCandidateSet *CandidateSet, + Expr *Range, ExprResult *CallExpr); + ExprResult BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, SourceLocation LParenLoc, @@ -1932,6 +1956,12 @@ public: Expr *ExecConfig, bool AllowTypoCorrection=true); + bool buildOverloadedCallSet(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, + Expr **Args, unsigned NumArgs, + SourceLocation RParenLoc, + OverloadCandidateSet *CandidateSet, + ExprResult *Result); + ExprResult CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned Opc, const UnresolvedSetImpl &Fns, @@ -2512,13 +2542,15 @@ public: StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, - SourceLocation RParenLoc); + SourceLocation RParenLoc, + bool ShouldTryDeref); StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *BeginEndDecl, Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, - SourceLocation RParenLoc); + SourceLocation RParenLoc, + bool ShouldTryDeref); StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body); StmtResult ActOnGotoStmt(SourceLocation GotoLoc, diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 0716f6f901ce..091d4aaaf208 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1441,7 +1441,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, FirstPart.take(), ForRangeInit.ColonLoc, ForRangeInit.RangeExpr.get(), - T.getCloseLocation()); + T.getCloseLocation(), true); // Similarly, we need to do the semantic analysis for a for-range diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 9382f7dddc6c..efc5b6eb0d5a 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9695,20 +9695,15 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, RParenLoc); } -/// ResolveOverloadedCallFn - Given the call expression that calls Fn -/// (which eventually refers to the declaration Func) and the call -/// arguments Args/NumArgs, attempt to resolve the function call down -/// to a specific function. If overload resolution succeeds, returns -/// the function declaration produced by overload -/// resolution. Otherwise, emits diagnostics, deletes all of the -/// arguments and Fn, and returns NULL. -ExprResult -Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, - SourceLocation LParenLoc, - Expr **Args, unsigned NumArgs, - SourceLocation RParenLoc, - Expr *ExecConfig, - bool AllowTypoCorrection) { +/// \brief Constructs and populates an OverloadedCandidateSet from +/// the given function. +/// \returns true when an the ExprResult output parameter has been set. +bool Sema::buildOverloadedCallSet(Scope *S, Expr *Fn, + UnresolvedLookupExpr *ULE, + Expr **Args, unsigned NumArgs, + SourceLocation RParenLoc, + OverloadCandidateSet *CandidateSet, + ExprResult *Result) { #ifndef NDEBUG if (ULE->requiresADL()) { // To do ADL, we must have found an unqualified name. @@ -9730,20 +9725,20 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, #endif UnbridgedCastsSet UnbridgedCasts; - if (checkArgPlaceholdersForOverload(*this, Args, NumArgs, UnbridgedCasts)) - return ExprError(); - - OverloadCandidateSet CandidateSet(Fn->getExprLoc()); + if (checkArgPlaceholdersForOverload(*this, Args, NumArgs, UnbridgedCasts)) { + *Result = ExprError(); + return true; + } // Add the functions denoted by the callee to the set of candidate // functions, including those from argument-dependent lookup. AddOverloadedCallCandidates(ULE, llvm::makeArrayRef(Args, NumArgs), - CandidateSet); + *CandidateSet); // If we found nothing, try to recover. // BuildRecoveryCallExpr diagnoses the error itself, so we just bail // out if it fails. - if (CandidateSet.empty()) { + if (CandidateSet->empty()) { // In Microsoft mode, if we are inside a template class member function then // create a type dependent CallExpr. The goal is to postpone name lookup // to instantiation time to be able to search into type dependent base @@ -9754,32 +9749,50 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, Context.DependentTy, VK_RValue, RParenLoc); CE->setTypeDependent(true); - return Owned(CE); + *Result = Owned(CE); + return true; } - return BuildRecoveryCallExpr(*this, S, Fn, ULE, LParenLoc, - llvm::MutableArrayRef(Args, NumArgs), - RParenLoc, /*EmptyLookup=*/true, - AllowTypoCorrection); + return false; } UnbridgedCasts.restore(); + return false; +} - OverloadCandidateSet::iterator Best; - switch (CandidateSet.BestViableFunction(*this, Fn->getLocStart(), Best)) { +/// FinishOverloadedCallExpr - given an OverloadCandidateSet, builds and returns +/// the completed call expression. If overload resolution fails, emits +/// diagnostics and returns ExprError() +static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, + UnresolvedLookupExpr *ULE, + SourceLocation LParenLoc, + Expr **Args, unsigned NumArgs, + SourceLocation RParenLoc, + Expr *ExecConfig, + OverloadCandidateSet *CandidateSet, + OverloadCandidateSet::iterator *Best, + OverloadingResult OverloadResult, + bool AllowTypoCorrection) { + if (CandidateSet->empty()) + return BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc, + llvm::MutableArrayRef(Args, NumArgs), + RParenLoc, /*EmptyLookup=*/true, + AllowTypoCorrection); + + switch (OverloadResult) { case OR_Success: { - FunctionDecl *FDecl = Best->Function; - MarkFunctionReferenced(Fn->getExprLoc(), FDecl); - CheckUnresolvedLookupAccess(ULE, Best->FoundDecl); - DiagnoseUseOfDecl(FDecl, ULE->getNameLoc()); - Fn = FixOverloadedFunctionReference(Fn, Best->FoundDecl, FDecl); - return BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs, RParenLoc, - ExecConfig); + FunctionDecl *FDecl = (*Best)->Function; + SemaRef.MarkFunctionReferenced(Fn->getExprLoc(), FDecl); + SemaRef.CheckUnresolvedLookupAccess(ULE, (*Best)->FoundDecl); + SemaRef.DiagnoseUseOfDecl(FDecl, ULE->getNameLoc()); + Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl); + return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs, + RParenLoc, ExecConfig); } case OR_No_Viable_Function: { // Try to recover by looking for viable functions which the user might // have meant to call. - ExprResult Recovery = BuildRecoveryCallExpr(*this, S, Fn, ULE, LParenLoc, + ExprResult Recovery = BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc, llvm::MutableArrayRef(Args, NumArgs), RParenLoc, /*EmptyLookup=*/false, @@ -9787,44 +9800,73 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, if (!Recovery.isInvalid()) return Recovery; - Diag(Fn->getLocStart(), + SemaRef.Diag(Fn->getLocStart(), diag::err_ovl_no_viable_function_in_call) << ULE->getName() << Fn->getSourceRange(); - CandidateSet.NoteCandidates(*this, OCD_AllCandidates, - llvm::makeArrayRef(Args, NumArgs)); + CandidateSet->NoteCandidates(SemaRef, OCD_AllCandidates, + llvm::makeArrayRef(Args, NumArgs)); break; } case OR_Ambiguous: - Diag(Fn->getLocStart(), diag::err_ovl_ambiguous_call) + SemaRef.Diag(Fn->getLocStart(), diag::err_ovl_ambiguous_call) << ULE->getName() << Fn->getSourceRange(); - CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, - llvm::makeArrayRef(Args, NumArgs)); + CandidateSet->NoteCandidates(SemaRef, OCD_ViableCandidates, + llvm::makeArrayRef(Args, NumArgs)); break; - case OR_Deleted: - { - Diag(Fn->getLocStart(), diag::err_ovl_deleted_call) - << Best->Function->isDeleted() - << ULE->getName() - << getDeletedOrUnavailableSuffix(Best->Function) - << Fn->getSourceRange(); - CandidateSet.NoteCandidates(*this, OCD_AllCandidates, - llvm::makeArrayRef(Args, NumArgs)); + case OR_Deleted: { + SemaRef.Diag(Fn->getLocStart(), diag::err_ovl_deleted_call) + << (*Best)->Function->isDeleted() + << ULE->getName() + << SemaRef.getDeletedOrUnavailableSuffix((*Best)->Function) + << Fn->getSourceRange(); + CandidateSet->NoteCandidates(SemaRef, OCD_AllCandidates, + llvm::makeArrayRef(Args, NumArgs)); - // We emitted an error for the unvailable/deleted function call but keep - // the call in the AST. - FunctionDecl *FDecl = Best->Function; - Fn = FixOverloadedFunctionReference(Fn, Best->FoundDecl, FDecl); - return BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs, - RParenLoc, ExecConfig); - } + // We emitted an error for the unvailable/deleted function call but keep + // the call in the AST. + FunctionDecl *FDecl = (*Best)->Function; + Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl); + return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs, + RParenLoc, ExecConfig); + } } // Overload resolution failed. return ExprError(); } +/// BuildOverloadedCallExpr - Given the call expression that calls Fn +/// (which eventually refers to the declaration Func) and the call +/// arguments Args/NumArgs, attempt to resolve the function call down +/// to a specific function. If overload resolution succeeds, returns +/// the call expression produced by overload resolution. +/// Otherwise, emits diagnostics and returns ExprError. +ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, + UnresolvedLookupExpr *ULE, + SourceLocation LParenLoc, + Expr **Args, unsigned NumArgs, + SourceLocation RParenLoc, + Expr *ExecConfig, + bool AllowTypoCorrection) { + OverloadCandidateSet CandidateSet(Fn->getExprLoc()); + ExprResult result; + + if (buildOverloadedCallSet(S, Fn, ULE, Args, NumArgs, LParenLoc, + &CandidateSet, &result)) + return result; + + OverloadCandidateSet::iterator Best; + OverloadingResult OverloadResult = + CandidateSet.BestViableFunction(*this, Fn->getLocStart(), Best); + + return FinishOverloadedCallExpr(*this, S, Fn, ULE, LParenLoc, Args, NumArgs, + RParenLoc, ExecConfig, &CandidateSet, + &Best, OverloadResult, + AllowTypoCorrection); +} + static bool IsOverloaded(const UnresolvedSetImpl &Functions) { return Functions.size() > 1 || (Functions.size() == 1 && isa(*Functions.begin())); @@ -11199,6 +11241,83 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R, return MaybeBindToTemporary(UDL); } +/// Build a call to 'begin' or 'end' for a C++11 for-range statement. If the +/// given LookupResult is non-empty, it is assumed to describe a member which +/// will be invoked. Otherwise, the function will be found via argument +/// dependent lookup. +/// CallExpr is set to a valid expression and FRS_Success returned on success, +/// otherwise CallExpr is set to ExprError() and some non-success value +/// is returned. +Sema::ForRangeStatus +Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc, + SourceLocation RangeLoc, VarDecl *Decl, + BeginEndFunction BEF, + const DeclarationNameInfo &NameInfo, + LookupResult &MemberLookup, + OverloadCandidateSet *CandidateSet, + Expr *Range, ExprResult *CallExpr) { + CandidateSet->clear(); + if (!MemberLookup.empty()) { + ExprResult MemberRef = + BuildMemberReferenceExpr(Range, Range->getType(), Loc, + /*IsPtr=*/false, CXXScopeSpec(), + /*TemplateKWLoc=*/SourceLocation(), + /*FirstQualifierInScope=*/0, + MemberLookup, + /*TemplateArgs=*/0); + if (MemberRef.isInvalid()) { + *CallExpr = ExprError(); + Diag(Range->getLocStart(), diag::note_in_for_range) + << RangeLoc << BEF << Range->getType(); + return FRS_DiagnosticIssued; + } + *CallExpr = ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(), Loc, 0); + if (CallExpr->isInvalid()) { + *CallExpr = ExprError(); + Diag(Range->getLocStart(), diag::note_in_for_range) + << RangeLoc << BEF << Range->getType(); + return FRS_DiagnosticIssued; + } + } else { + UnresolvedSet<0> FoundNames; + // C++11 [stmt.ranged]p1: For the purposes of this name lookup, namespace + // std is an associated namespace. + UnresolvedLookupExpr *Fn = + UnresolvedLookupExpr::Create(Context, /*NamingClass=*/0, + NestedNameSpecifierLoc(), NameInfo, + /*NeedsADL=*/true, /*Overloaded=*/false, + FoundNames.begin(), FoundNames.end(), + /*LookInStdNamespace=*/true); + + bool CandidateSetError = buildOverloadedCallSet(S, Fn, Fn, &Range, 1, Loc, + CandidateSet, CallExpr); + if (CandidateSet->empty() || CandidateSetError) { + *CallExpr = ExprError(); + return FRS_NoViableFunction; + } + OverloadCandidateSet::iterator Best; + OverloadingResult OverloadResult = + CandidateSet->BestViableFunction(*this, Fn->getLocStart(), Best); + + if (OverloadResult == OR_No_Viable_Function) { + *CallExpr = ExprError(); + return FRS_NoViableFunction; + } + *CallExpr = FinishOverloadedCallExpr(*this, S, Fn, Fn, Loc, &Range, 1, + Loc, 0, CandidateSet, &Best, + OverloadResult, + /*AllowTypoCorrection=*/false); + if (CallExpr->isInvalid() || OverloadResult != OR_Success) { + *CallExpr = ExprError(); + Diag(Range->getLocStart(), diag::note_in_for_range) + << RangeLoc << BEF << Range->getType(); + return FRS_DiagnosticIssued; + } + } + return FRS_Success; +} + + /// FixOverloadedFunctionReference - E is an expression that refers to /// a C++ overloaded function (possibly with some parentheses and /// perhaps a '&' around it). We have resolved the overloaded function diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index bb025a4fb31a..8367c1a22042 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1549,25 +1549,6 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, ForLoc, RParenLoc)); } -namespace { - -enum BeginEndFunction { - BEF_begin, - BEF_end -}; - -/// Build a variable declaration for a for-range statement. -static VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, - QualType Type, const char *Name) { - DeclContext *DC = SemaRef.CurContext; - IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name); - TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc); - VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type, - TInfo, SC_Auto, SC_None); - Decl->setImplicit(); - return Decl; -} - /// Finish building a variable declaration for a for-range statement. /// \return true if an error occurs. static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, @@ -1600,12 +1581,14 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, return false; } +namespace { + /// Produce a note indicating which begin/end function was implicitly called -/// by a C++0x for-range statement. This is often not obvious from the code, +/// by a C++11 for-range statement. This is often not obvious from the code, /// nor from the diagnostics produced when analysing the implicit expressions /// required in a for-range statement. void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E, - BeginEndFunction BEF) { + Sema::BeginEndFunction BEF) { CallExpr *CE = dyn_cast(E); if (!CE) return; @@ -1626,56 +1609,16 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E, << BEF << IsTemplate << Description << E->getType(); } -/// Build a call to 'begin' or 'end' for a C++0x for-range statement. If the -/// given LookupResult is non-empty, it is assumed to describe a member which -/// will be invoked. Otherwise, the function will be found via argument -/// dependent lookup. -static ExprResult BuildForRangeBeginEndCall(Sema &SemaRef, Scope *S, - SourceLocation Loc, - VarDecl *Decl, - BeginEndFunction BEF, - const DeclarationNameInfo &NameInfo, - LookupResult &MemberLookup, - Expr *Range) { - ExprResult CallExpr; - if (!MemberLookup.empty()) { - ExprResult MemberRef = - SemaRef.BuildMemberReferenceExpr(Range, Range->getType(), Loc, - /*IsPtr=*/false, CXXScopeSpec(), - /*TemplateKWLoc=*/SourceLocation(), - /*FirstQualifierInScope=*/0, - MemberLookup, - /*TemplateArgs=*/0); - if (MemberRef.isInvalid()) - return ExprError(); - CallExpr = SemaRef.ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(), - Loc, 0); - if (CallExpr.isInvalid()) - return ExprError(); - } else { - UnresolvedSet<0> FoundNames; - // C++0x [stmt.ranged]p1: For the purposes of this name lookup, namespace - // std is an associated namespace. - UnresolvedLookupExpr *Fn = - UnresolvedLookupExpr::Create(SemaRef.Context, /*NamingClass=*/0, - NestedNameSpecifierLoc(), NameInfo, - /*NeedsADL=*/true, /*Overloaded=*/false, - FoundNames.begin(), FoundNames.end(), - /*LookInStdNamespace=*/true); - CallExpr = SemaRef.BuildOverloadedCallExpr(S, Fn, Fn, Loc, &Range, 1, Loc, - 0, /*AllowTypoCorrection=*/false); - if (CallExpr.isInvalid()) { - SemaRef.Diag(Range->getLocStart(), diag::note_for_range_type) - << Range->getType(); - return ExprError(); - } - } - if (FinishForRangeVarDecl(SemaRef, Decl, CallExpr.get(), Loc, - diag::err_for_range_iter_deduction_failure)) { - NoteForRangeBeginEndFunction(SemaRef, CallExpr.get(), BEF); - return ExprError(); - } - return CallExpr; +/// Build a variable declaration for a for-range statement. +VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, + QualType Type, const char *Name) { + DeclContext *DC = SemaRef.CurContext; + IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name); + TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc); + VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type, + TInfo, SC_Auto, SC_None); + Decl->setImplicit(); + return Decl; } } @@ -1706,7 +1649,7 @@ static bool ObjCEnumerationCollection(Expr *Collection) { StmtResult Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, Stmt *First, SourceLocation ColonLoc, Expr *Range, - SourceLocation RParenLoc) { + SourceLocation RParenLoc, bool ShouldTryDeref) { if (!First || !Range) return StmtError(); @@ -1744,7 +1687,111 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, return BuildCXXForRangeStmt(ForLoc, ColonLoc, RangeDecl.get(), /*BeginEndDecl=*/0, /*Cond=*/0, /*Inc=*/0, DS, - RParenLoc); + RParenLoc, ShouldTryDeref); +} + +/// \brief Create the initialization, compare, and increment steps for +/// the range-based for loop expression. +/// This function does not handle array-based for loops, +/// which are created in Sema::BuildCXXForRangeStmt. +/// +/// \returns a ForRangeStatus indicating success or what kind of error occurred. +/// BeginExpr and EndExpr are set and FRS_Success is returned on success; +/// CandidateSet and BEF are set and some non-success value is returned on +/// failure. +static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S, + Expr *BeginRange, Expr *EndRange, + QualType RangeType, + VarDecl *BeginVar, + VarDecl *EndVar, + SourceLocation ColonLoc, + OverloadCandidateSet *CandidateSet, + ExprResult *BeginExpr, + ExprResult *EndExpr, + Sema::BeginEndFunction *BEF) { + DeclarationNameInfo BeginNameInfo( + &SemaRef.PP.getIdentifierTable().get("begin"), ColonLoc); + DeclarationNameInfo EndNameInfo(&SemaRef.PP.getIdentifierTable().get("end"), + ColonLoc); + + LookupResult BeginMemberLookup(SemaRef, BeginNameInfo, + Sema::LookupMemberName); + LookupResult EndMemberLookup(SemaRef, EndNameInfo, Sema::LookupMemberName); + + 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 + // lookup (3.4.5), and if either (or both) finds at least one + // declaration, begin-expr and end-expr are __range.begin() and + // __range.end(), respectively; + SemaRef.LookupQualifiedName(BeginMemberLookup, D); + SemaRef.LookupQualifiedName(EndMemberLookup, D); + + if (BeginMemberLookup.empty() != EndMemberLookup.empty()) { + SourceLocation RangeLoc = BeginVar->getLocation(); + *BEF = BeginMemberLookup.empty() ? Sema::BEF_end : Sema::BEF_begin; + + SemaRef.Diag(RangeLoc, diag::err_for_range_member_begin_end_mismatch) + << RangeLoc << BeginRange->getType() << *BEF; + return Sema::FRS_DiagnosticIssued; + } + } 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 = Sema::BEF_begin; + Sema::ForRangeStatus RangeStatus = + SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, BeginVar, + Sema::BEF_begin, BeginNameInfo, + BeginMemberLookup, CandidateSet, + BeginRange, BeginExpr); + + if (RangeStatus != Sema::FRS_Success) + return RangeStatus; + if (FinishForRangeVarDecl(SemaRef, BeginVar, BeginExpr->get(), ColonLoc, + diag::err_for_range_iter_deduction_failure)) { + NoteForRangeBeginEndFunction(SemaRef, BeginExpr->get(), *BEF); + return Sema::FRS_DiagnosticIssued; + } + + *BEF = Sema::BEF_end; + RangeStatus = + SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, EndVar, + Sema::BEF_end, EndNameInfo, + EndMemberLookup, CandidateSet, + EndRange, EndExpr); + if (RangeStatus != Sema::FRS_Success) + 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; +} + +/// Speculatively attempt to dereference an invalid range expression. +/// This function will not emit diagnostics, but returns StmtError if +/// an error occurs. +static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S, + SourceLocation ForLoc, + Stmt *LoopVarDecl, + SourceLocation ColonLoc, + Expr *Range, + SourceLocation RangeLoc, + SourceLocation RParenLoc) { + Sema::SFINAETrap Trap(SemaRef); + ExprResult AdjustedRange = SemaRef.BuildUnaryOp(S, RangeLoc, UO_Deref, Range); + StmtResult SR = + SemaRef.ActOnCXXForRangeStmt(ForLoc, LoopVarDecl, ColonLoc, + AdjustedRange.get(), RParenLoc, false); + if (Trap.hasErrorOccurred()) + return StmtError(); + return SR; } /// BuildCXXForRangeStmt - Build or instantiate a C++0x for-range statement. @@ -1752,7 +1799,7 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *BeginEnd, Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, - SourceLocation RParenLoc) { + SourceLocation RParenLoc, bool ShouldTryDeref) { Scope *S = getCurScope(); DeclStmt *RangeDS = cast(RangeDecl); @@ -1838,50 +1885,49 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, return StmtError(); } } else { - DeclarationNameInfo BeginNameInfo(&PP.getIdentifierTable().get("begin"), - ColonLoc); - DeclarationNameInfo EndNameInfo(&PP.getIdentifierTable().get("end"), - ColonLoc); + OverloadCandidateSet CandidateSet(RangeLoc); + Sema::BeginEndFunction BEFFailure; + ForRangeStatus RangeStatus = + BuildNonArrayForRange(*this, S, BeginRangeRef.get(), + EndRangeRef.get(), RangeType, + BeginVar, EndVar, ColonLoc, &CandidateSet, + &BeginExpr, &EndExpr, &BEFFailure); - LookupResult BeginMemberLookup(*this, BeginNameInfo, LookupMemberName); - LookupResult EndMemberLookup(*this, EndNameInfo, LookupMemberName); - - 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 - // lookup (3.4.5), and if either (or both) finds at least one - // declaration, begin-expr and end-expr are __range.begin() and - // __range.end(), respectively; - LookupQualifiedName(BeginMemberLookup, D); - LookupQualifiedName(EndMemberLookup, D); - - if (BeginMemberLookup.empty() != EndMemberLookup.empty()) { - Diag(ColonLoc, diag::err_for_range_member_begin_end_mismatch) - << RangeType << BeginMemberLookup.empty(); - return StmtError(); + // If building the range failed, try dereferencing the range expression + // unless a diagnostic was issued or the end function is problematic. + if (ShouldTryDeref && RangeStatus == FRS_NoViableFunction && + BEFFailure == BEF_begin) { + StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc, + LoopVarDecl, ColonLoc, + Range, RangeLoc, + RParenLoc); + if (!SR.isInvalid()) { + // The attempt to dereference would succeed; return the result of + // recovery. + Diag(RangeLoc, diag::err_for_range_dereference) + << RangeLoc << RangeType + << FixItHint::CreateInsertion(RangeLoc, "*"); + return SR; } - } 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. } - BeginExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, BeginVar, - BEF_begin, BeginNameInfo, - BeginMemberLookup, - BeginRangeRef.get()); - if (BeginExpr.isInvalid()) - return StmtError(); - - EndExpr = BuildForRangeBeginEndCall(*this, S, ColonLoc, EndVar, - BEF_end, EndNameInfo, - EndMemberLookup, EndRangeRef.get()); - if (EndExpr.isInvalid()) + // Otherwise, emit diagnostics if we haven't already. + if (RangeStatus == FRS_NoViableFunction) { + Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get(); + Diag(Range->getLocStart(), diag::err_for_range_invalid) + << RangeLoc << Range->getType() << BEFFailure; + CandidateSet.NoteCandidates(*this, OCD_AllCandidates, + llvm::makeArrayRef(&Range, /*NumArgs=*/1)); + } + // Return an error if no fix was discovered. + if (RangeStatus != FRS_Success) return StmtError(); } - // C++0x [decl.spec.auto]p6: BeginType and EndType must be the same. + assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() && + "invalid range expression in for loop"); + + // C++11 [dcl.spec.auto]p7: BeginType and EndType must be the same. QualType BeginType = BeginVar->getType(), EndType = EndVar->getType(); if (!Context.hasSameType(BeginType, EndType)) { Diag(RangeLoc, diag::err_for_range_begin_end_types_differ) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 05a3c611e9f8..391c0b3f3b5e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1338,7 +1338,7 @@ public: Stmt *LoopVar, SourceLocation RParenLoc) { return getSema().BuildCXXForRangeStmt(ForLoc, ColonLoc, Range, BeginEnd, - Cond, Inc, LoopVar, RParenLoc); + Cond, Inc, LoopVar, RParenLoc, false); } /// \brief Build a new C++0x range-based for statement. diff --git a/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp index 96bb47212274..66e30f5dc63f 100644 --- a/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp +++ b/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp @@ -3,7 +3,7 @@ struct pr12960 { int begin; void foo(int x) { - for (int& it : x) { // expected-error {{use of undeclared identifier 'begin'}} expected-note {{range has type 'int'}} + for (int& it : x) { // expected-error {{invalid range expression of type 'int'; no viable 'begin' function available}} } } }; @@ -116,9 +116,9 @@ void g() { struct NoEndADL { null_t alt_begin(); }; - for (auto u : NoBeginADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type 'NoBeginADL'}} + for (auto u : NoBeginADL()) { // expected-error {{invalid range expression of type 'NoBeginADL'; no viable 'begin' function available}} } - for (auto u : NoEndADL()) { // expected-error {{no matching function for call to 'end'}} expected-note {{range has type 'NoEndADL'}} + for (auto u : NoEndADL()) { // expected-error {{invalid range expression of type 'NoEndADL'; no viable 'end' function available}} } struct NoBegin { @@ -156,8 +156,7 @@ void g() { for (int n : NoCopy()) { // ok } - for (int n : 42) { // expected-error {{no matching function for call to 'begin'}} \ - expected-note {{range has type 'int'}} + for (int n : 42) { // expected-error {{invalid range expression of type 'int'; no viable 'begin' function available}} } for (auto a : *also_incomplete) { // expected-error {{cannot use incomplete type 'struct Incomplete' as a range}} @@ -179,9 +178,10 @@ template void h(A(&)[13]); // expected-note {{requested here}} template void i(T t) { - for (auto u : t) { // expected-error {{no matching function for call to 'begin'}} \ + for (auto u : t) { // expected-error {{invalid range expression of type 'A *'; no viable 'begin' function available}} \ expected-error {{member function 'begin' not viable}} \ - expected-note {{range has type}} + expected-note {{when looking up 'begin' function}} + } } template void i(A*); // expected-note {{requested here}} @@ -204,9 +204,10 @@ void end(VoidBeginADL); void j() { for (auto u : NS::ADL()) { } - for (auto u : NS::NoADL()) { // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}} + for (auto u : NS::NoADL()) { // expected-error {{invalid range expression of type 'NS::NoADL'; no viable 'begin' function available}} } for (auto a : VoidBeginADL()) { // expected-error {{cannot use type 'void' as an iterator}} + } } @@ -215,4 +216,3 @@ void example() { for (int &x : array) x *= 2; } - diff --git a/clang/test/SemaCXX/for-range-dereference.cpp b/clang/test/SemaCXX/for-range-dereference.cpp new file mode 100644 index 000000000000..3bf50f333002 --- /dev/null +++ b/clang/test/SemaCXX/for-range-dereference.cpp @@ -0,0 +1,86 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s +struct Data { }; +struct T { + Data *begin(); + Data *end(); +}; + +struct NoBegin { + Data *end(); +}; + +struct DeletedEnd : public T { + Data *begin(); + Data *end() = delete; //expected-note {{function has been explicitly marked deleted here}} +}; + +struct DeletedADLBegin { }; + +int* begin(DeletedADLBegin) = delete; //expected-note {{candidate function has been explicitly deleted}} \ + expected-note 6 {{candidate function not viable: no known conversion}} + +struct PrivateEnd { + Data *begin(); + + private: + Data *end(); // expected-note 1 {{declared private here}} +}; + +struct ADLNoEnd { }; +Data * begin(ADLNoEnd); // expected-note 7 {{candidate function not viable: no known conversion}} + +struct OverloadedStar { + T operator*(); +}; + +void f() { + T t; + for (auto i : t) { } + T *pt; + for (auto i : pt) { } // expected-error{{invalid range expression of type 'T *'; did you mean to dereference it with '*'?}} + + int arr[10]; + for (auto i : arr) { } + int (*parr)[10]; + for (auto i : parr) { }// expected-error{{invalid range expression of type 'int (*)[10]'; did you mean to dereference it with '*'?}} + + NoBegin NB; + for (auto i : NB) { }// expected-error{{range type 'NoBegin' has 'end' member but no 'begin' member}} + NoBegin *pNB; + for (auto i : pNB) { }// expected-error{{invalid range expression of type 'NoBegin *'; no viable 'begin' function available}} + NoBegin **ppNB; + for (auto i : ppNB) { }// expected-error{{invalid range expression of type 'NoBegin **'; no viable 'begin' function available}} + NoBegin *****pppppNB; + for (auto i : pppppNB) { }// expected-error{{invalid range expression of type 'NoBegin *****'; no viable 'begin' function available}} + + ADLNoEnd ANE; + for (auto i : ANE) { } // expected-error{{invalid range expression of type 'ADLNoEnd'; no viable 'end' function available}} + ADLNoEnd *pANE; + for (auto i : pANE) { } // expected-error{{invalid range expression of type 'ADLNoEnd *'; no viable 'begin' function available}} + + DeletedEnd DE; + for (auto i : DE) { } // expected-error{{attempt to use a deleted function}} \ +expected-note {{when looking up 'end' function for range expression of type 'DeletedEnd'}} + DeletedEnd *pDE; + + for (auto i : pDE) { } // expected-error {{invalid range expression of type 'DeletedEnd *'; no viable 'begin' function available}} + + PrivateEnd PE; + // FIXME: This diagnostic should be improved, as it does not specify that + // the range is invalid. + for (auto i : PE) { } // expected-error{{'end' is a private member of 'PrivateEnd'}} + + // FIXME: This diagnostic should be improved as well. It should not mention a + // deleted function, and we should not issue a FixIt suggesting a dereference. + PrivateEnd *pPE; + for (auto i : pPE) { }// expected-error {{invalid range expression of type 'PrivateEnd *'}} + + DeletedADLBegin DAB; + for (auto i : DAB) { } // expected-error {{call to deleted function 'begin'}}\ + expected-note {{when looking up 'begin' function for range expression of type 'DeletedADLBegin'}} + + OverloadedStar OS; + for (auto i : *OS) { } + + for (auto i : OS) { } // expected-error {{invalid range expression of type 'OverloadedStar'; did you mean to dereference it with '*'?}} +} diff --git a/clang/test/SemaCXX/for-range-no-std.cpp b/clang/test/SemaCXX/for-range-no-std.cpp index fa42ca45a58f..66b445e4d83b 100644 --- a/clang/test/SemaCXX/for-range-no-std.cpp +++ b/clang/test/SemaCXX/for-range-no-std.cpp @@ -31,10 +31,10 @@ NS::iter end(NS::NoADL); void f() { int a[] = {1, 2, 3}; for (auto b : S()) {} // ok - for (auto b : T()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}} + for (auto b : T()) {} // expected-error {{invalid range expression of type 'T'}} for (auto b : a) {} // ok for (int b : NS::ADL()) {} // ok - for (int b : NS::NoADL()) {} // expected-error {{no matching function for call to 'begin'}} expected-note {{range has type}} + for (int b : NS::NoADL()) {} // expected-error {{invalid range expression of type 'NS::NoADL'}} } void PR11601() { diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index 919edca831f4..c21ef51a7da5 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -155,7 +155,7 @@ void Test3() { struct R {}; bool begun(R); void RangeTest() { - for (auto b : R()) {} // expected-error {{use of undeclared identifier 'begin'}} expected-note {{range has type}} + for (auto b : R()) {} // expected-error {{invalid range expression of type 'R'}} } // PR 12019 - Avoid infinite mutual recursion in DiagnoseInvalidRedeclaration