Better diagnostics for range-based for loops with bad range types.

The old error message stating that 'begin' was an undeclared identifier
is replaced with a new message explaining that the error is in the range
expression, along with which of the begin() and end() functions was
problematic if relevant.

Additionally, if the range was a pointer type or defines operator*,
attempt to dereference the range, and offer a FixIt if the modified range
works.

llvm-svn: 162248
This commit is contained in:
Sam Panzer 2012-08-21 00:52:01 +00:00
parent 74e6f9fc65
commit 0f38443616
10 changed files with 476 additions and 185 deletions

View File

@ -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">;

View File

@ -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,

View File

@ -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

View File

@ -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<Expr *>(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<Expr *>(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<Expr *>(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<FunctionTemplateDecl>(*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

View File

@ -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<CallExpr>(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<DeclStmt>(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)

View File

@ -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.

View File

@ -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], int>(A(&)[13]); // expected-note {{requested here}}
template<typename T>
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[13]>(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;
}

View File

@ -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 '*'?}}
}

View File

@ -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() {

View File

@ -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