PR43080: Do not build context-sensitive expressions during name classification.

Summary:
We don't know what context to use until the classification result is
consumed by the parser, which could happen in a different semantic
context. So don't build the expression that results from name
classification until we get to that point and can handle it properly.

This covers everything except C++ implicit class member access, which
is a little awkward to handle properly in the face of the protected
member access check. But it at least fixes all the currently-filed
instances of PR43080.

Reviewers: efriedma

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D68896

llvm-svn: 374826
This commit is contained in:
Richard Smith 2019-10-14 21:53:03 +00:00
parent 9efbc564ba
commit 7e8fe67f0e
14 changed files with 417 additions and 178 deletions

View File

@ -728,6 +728,11 @@ ANNOTATION(typename) // annotation for a C typedef name, a C++ (possibly
ANNOTATION(template_id) // annotation for a C++ template-id that names a ANNOTATION(template_id) // annotation for a C++ template-id that names a
// function template specialization (not a type), // function template specialization (not a type),
// e.g., "std::swap<int>" // e.g., "std::swap<int>"
ANNOTATION(non_type) // annotation for a single non-type declaration
ANNOTATION(non_type_undeclared) // annotation for an undeclared identifier that
// was assumed to be an ADL-only function name
ANNOTATION(non_type_dependent) // annotation for an assumed non-type member of
// a dependent base class
ANNOTATION(primary_expr) // annotation for a primary expression ANNOTATION(primary_expr) // annotation for a primary expression
ANNOTATION(decltype) // annotation for a decltype expression, ANNOTATION(decltype) // annotation for a decltype expression,
// e.g., "decltype(foo.bar())" // e.g., "decltype(foo.bar())"

View File

@ -766,6 +766,22 @@ private:
Tok.setAnnotationValue(T.getAsOpaquePtr()); Tok.setAnnotationValue(T.getAsOpaquePtr());
} }
static NamedDecl *getNonTypeAnnotation(const Token &Tok) {
return static_cast<NamedDecl*>(Tok.getAnnotationValue());
}
static void setNonTypeAnnotation(Token &Tok, NamedDecl *ND) {
Tok.setAnnotationValue(ND);
}
static IdentifierInfo *getIdentifierAnnotation(const Token &Tok) {
return static_cast<IdentifierInfo*>(Tok.getAnnotationValue());
}
static void setIdentifierAnnotation(Token &Tok, IdentifierInfo *ND) {
Tok.setAnnotationValue(ND);
}
/// Read an already-translated primary expression out of an annotation /// Read an already-translated primary expression out of an annotation
/// token. /// token.
static ExprResult getExprAnnotation(const Token &Tok) { static ExprResult getExprAnnotation(const Token &Tok) {
@ -799,8 +815,7 @@ private:
/// Annotation was successful. /// Annotation was successful.
ANK_Success ANK_Success
}; };
AnnotatedNameKind TryAnnotateName(bool IsAddressOfOperand, AnnotatedNameKind TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr);
CorrectionCandidateCallback *CCC = nullptr);
/// Push a tok::annot_cxxscope token onto the token stream. /// Push a tok::annot_cxxscope token onto the token stream.
void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation); void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation);

View File

@ -1855,29 +1855,52 @@ public:
/// Describes the result of the name lookup and resolution performed /// Describes the result of the name lookup and resolution performed
/// by \c ClassifyName(). /// by \c ClassifyName().
enum NameClassificationKind { enum NameClassificationKind {
/// This name is not a type or template in this context, but might be
/// something else.
NC_Unknown, NC_Unknown,
/// Classification failed; an error has been produced.
NC_Error, NC_Error,
/// The name has been typo-corrected to a keyword.
NC_Keyword, NC_Keyword,
/// The name was classified as a type.
NC_Type, NC_Type,
NC_Expression, /// The name was classified as a specific non-type, non-template
NC_NestedNameSpecifier, /// declaration. ActOnNameClassifiedAsNonType should be called to
/// convert the declaration to an expression.
NC_NonType,
/// The name was classified as an ADL-only function name.
/// ActOnNameClassifiedAsUndeclaredNonType should be called to convert the
/// result to an expression.
NC_UndeclaredNonType,
/// The name denotes a member of a dependent type that could not be
/// resolved. ActOnNameClassifiedAsDependentNonType should be called to
/// convert the result to an expression.
NC_DependentNonType,
/// The name was classified as a non-type, and an expression representing
/// that name has been formed.
NC_ContextIndependentExpr,
/// The name was classified as a template whose specializations are types.
NC_TypeTemplate, NC_TypeTemplate,
/// The name was classified as a variable template name.
NC_VarTemplate, NC_VarTemplate,
/// The name was classified as a function template name.
NC_FunctionTemplate, NC_FunctionTemplate,
/// The name was classified as an ADL-only function template name.
NC_UndeclaredTemplate, NC_UndeclaredTemplate,
}; };
class NameClassification { class NameClassification {
NameClassificationKind Kind; NameClassificationKind Kind;
ExprResult Expr; union {
TemplateName Template; ExprResult Expr;
ParsedType Type; NamedDecl *NonTypeDecl;
TemplateName Template;
ParsedType Type;
};
explicit NameClassification(NameClassificationKind Kind) : Kind(Kind) {} explicit NameClassification(NameClassificationKind Kind) : Kind(Kind) {}
public: public:
NameClassification(ExprResult Expr) : Kind(NC_Expression), Expr(Expr) {}
NameClassification(ParsedType Type) : Kind(NC_Type), Type(Type) {} NameClassification(ParsedType Type) : Kind(NC_Type), Type(Type) {}
NameClassification(const IdentifierInfo *Keyword) : Kind(NC_Keyword) {} NameClassification(const IdentifierInfo *Keyword) : Kind(NC_Keyword) {}
@ -1890,8 +1913,24 @@ public:
return NameClassification(NC_Unknown); return NameClassification(NC_Unknown);
} }
static NameClassification NestedNameSpecifier() { static NameClassification ContextIndependentExpr(ExprResult E) {
return NameClassification(NC_NestedNameSpecifier); NameClassification Result(NC_ContextIndependentExpr);
Result.Expr = E;
return Result;
}
static NameClassification NonType(NamedDecl *D) {
NameClassification Result(NC_NonType);
Result.NonTypeDecl = D;
return Result;
}
static NameClassification UndeclaredNonType() {
return NameClassification(NC_UndeclaredNonType);
}
static NameClassification DependentNonType() {
return NameClassification(NC_DependentNonType);
} }
static NameClassification TypeTemplate(TemplateName Name) { static NameClassification TypeTemplate(TemplateName Name) {
@ -1920,14 +1959,19 @@ public:
NameClassificationKind getKind() const { return Kind; } NameClassificationKind getKind() const { return Kind; }
ExprResult getExpression() const {
assert(Kind == NC_ContextIndependentExpr);
return Expr;
}
ParsedType getType() const { ParsedType getType() const {
assert(Kind == NC_Type); assert(Kind == NC_Type);
return Type; return Type;
} }
ExprResult getExpression() const { NamedDecl *getNonTypeDecl() const {
assert(Kind == NC_Expression); assert(Kind == NC_NonType);
return Expr; return NonTypeDecl;
} }
TemplateName getTemplateName() const { TemplateName getTemplateName() const {
@ -1971,17 +2015,29 @@ public:
/// \param NextToken The token following the identifier. Used to help /// \param NextToken The token following the identifier. Used to help
/// disambiguate the name. /// disambiguate the name.
/// ///
/// \param IsAddressOfOperand True if this name is the operand of a unary
/// address of ('&') expression, assuming it is classified as an
/// expression.
///
/// \param CCC The correction callback, if typo correction is desired. /// \param CCC The correction callback, if typo correction is desired.
NameClassification ClassifyName(Scope *S, CXXScopeSpec &SS, NameClassification ClassifyName(Scope *S, CXXScopeSpec &SS,
IdentifierInfo *&Name, SourceLocation NameLoc, IdentifierInfo *&Name, SourceLocation NameLoc,
const Token &NextToken, const Token &NextToken,
bool IsAddressOfOperand,
CorrectionCandidateCallback *CCC = nullptr); CorrectionCandidateCallback *CCC = nullptr);
/// Act on the result of classifying a name as an undeclared (ADL-only)
/// non-type declaration.
ExprResult ActOnNameClassifiedAsUndeclaredNonType(IdentifierInfo *Name,
SourceLocation NameLoc);
/// Act on the result of classifying a name as an undeclared member of a
/// dependent base class.
ExprResult ActOnNameClassifiedAsDependentNonType(const CXXScopeSpec &SS,
IdentifierInfo *Name,
SourceLocation NameLoc,
bool IsAddressOfOperand);
/// Act on the result of classifying a name as a specific non-type
/// declaration.
ExprResult ActOnNameClassifiedAsNonType(Scope *S, const CXXScopeSpec &SS,
NamedDecl *Found,
SourceLocation NameLoc,
const Token &NextToken);
/// Describes the detailed kind of a template name. Used in diagnostics. /// Describes the detailed kind of a template name. Used in diagnostics.
enum class TemplateNameKindForDiagnostics { enum class TemplateNameKindForDiagnostics {
ClassTemplate, ClassTemplate,
@ -3407,6 +3463,7 @@ public:
LookupNameKind NameKind, LookupNameKind NameKind,
RedeclarationKind Redecl RedeclarationKind Redecl
= NotForRedeclaration); = NotForRedeclaration);
bool LookupBuiltin(LookupResult &R);
bool LookupName(LookupResult &R, Scope *S, bool LookupName(LookupResult &R, Scope *S,
bool AllowBuiltinCreation = false); bool AllowBuiltinCreation = false);
bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
@ -4389,6 +4446,10 @@ public:
TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr, TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr,
ArrayRef<Expr *> Args = None, TypoExpr **Out = nullptr); ArrayRef<Expr *> Args = None, TypoExpr **Out = nullptr);
DeclResult LookupIvarInObjCMethod(LookupResult &Lookup, Scope *S,
IdentifierInfo *II);
ExprResult BuildIvarRefExpr(Scope *S, SourceLocation Loc, ObjCIvarDecl *IV);
ExprResult LookupInObjCMethod(LookupResult &LookUp, Scope *S, ExprResult LookupInObjCMethod(LookupResult &LookUp, Scope *S,
IdentifierInfo *II, IdentifierInfo *II,
bool AllowBuiltinCreation=false); bool AllowBuiltinCreation=false);

View File

@ -2929,28 +2929,29 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
IdentifierInfo *Name = AfterScope.getIdentifierInfo(); IdentifierInfo *Name = AfterScope.getIdentifierInfo();
Sema::NameClassification Classification = Actions.ClassifyName( Sema::NameClassification Classification = Actions.ClassifyName(
getCurScope(), SS, Name, AfterScope.getLocation(), Next, getCurScope(), SS, Name, AfterScope.getLocation(), Next,
/*IsAddressOfOperand=*/false, /*CCC=*/nullptr); /*CCC=*/nullptr);
switch (Classification.getKind()) { switch (Classification.getKind()) {
case Sema::NC_Error: case Sema::NC_Error:
SkipMalformedDecl(); SkipMalformedDecl();
return true; return true;
case Sema::NC_Keyword: case Sema::NC_Keyword:
case Sema::NC_NestedNameSpecifier: llvm_unreachable("typo correction is not possible here");
llvm_unreachable("typo correction and nested name specifiers not "
"possible here");
case Sema::NC_Type: case Sema::NC_Type:
case Sema::NC_TypeTemplate: case Sema::NC_TypeTemplate:
case Sema::NC_UndeclaredNonType:
case Sema::NC_UndeclaredTemplate:
// Not a previously-declared non-type entity. // Not a previously-declared non-type entity.
MightBeDeclarator = false; MightBeDeclarator = false;
break; break;
case Sema::NC_Unknown: case Sema::NC_Unknown:
case Sema::NC_Expression: case Sema::NC_NonType:
case Sema::NC_DependentNonType:
case Sema::NC_ContextIndependentExpr:
case Sema::NC_VarTemplate: case Sema::NC_VarTemplate:
case Sema::NC_FunctionTemplate: case Sema::NC_FunctionTemplate:
case Sema::NC_UndeclaredTemplate:
// Might be a redeclaration of a prior entity. // Might be a redeclaration of a prior entity.
break; break;
} }

View File

@ -840,13 +840,23 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
return Actions.ActOnCXXNullPtrLiteral(ConsumeToken()); return Actions.ActOnCXXNullPtrLiteral(ConsumeToken());
case tok::annot_primary_expr: case tok::annot_primary_expr:
assert(Res.get() == nullptr && "Stray primary-expression annotation?");
Res = getExprAnnotation(Tok); Res = getExprAnnotation(Tok);
ConsumeAnnotationToken(); ConsumeAnnotationToken();
if (!Res.isInvalid() && Tok.is(tok::less)) if (!Res.isInvalid() && Tok.is(tok::less))
checkPotentialAngleBracket(Res); checkPotentialAngleBracket(Res);
break; break;
case tok::annot_non_type:
case tok::annot_non_type_dependent:
case tok::annot_non_type_undeclared: {
CXXScopeSpec SS;
Token Replacement;
Res = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
assert(!Res.isUnset() &&
"should not perform typo correction on annotation token");
break;
}
case tok::kw___super: case tok::kw___super:
case tok::kw_decltype: case tok::kw_decltype:
// Annotate the token and tail recurse. // Annotate the token and tail recurse.

View File

@ -555,27 +555,66 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
return false; return false;
} }
ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand, ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
bool isAddressOfOperand,
Token &Replacement) { Token &Replacement) {
SourceLocation TemplateKWLoc; ExprResult E;
UnqualifiedId Name;
if (ParseUnqualifiedId(SS,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false,
/*ObjectType=*/nullptr, &TemplateKWLoc, Name))
return ExprError();
// This is only the direct operand of an & operator if it is not // We may have already annotated this id-expression.
// followed by a postfix-expression suffix. switch (Tok.getKind()) {
if (isAddressOfOperand && isPostfixExpressionSuffixStart()) case tok::annot_non_type: {
isAddressOfOperand = false; NamedDecl *ND = getNonTypeAnnotation(Tok);
SourceLocation Loc = ConsumeAnnotationToken();
E = Actions.ActOnNameClassifiedAsNonType(getCurScope(), SS, ND, Loc, Tok);
break;
}
case tok::annot_non_type_dependent: {
IdentifierInfo *II = getIdentifierAnnotation(Tok);
SourceLocation Loc = ConsumeAnnotationToken();
// This is only the direct operand of an & operator if it is not
// followed by a postfix-expression suffix.
if (isAddressOfOperand && isPostfixExpressionSuffixStart())
isAddressOfOperand = false;
E = Actions.ActOnNameClassifiedAsDependentNonType(SS, II, Loc,
isAddressOfOperand);
break;
}
case tok::annot_non_type_undeclared: {
assert(SS.isEmpty() &&
"undeclared non-type annotation should be unqualified");
IdentifierInfo *II = getIdentifierAnnotation(Tok);
SourceLocation Loc = ConsumeAnnotationToken();
E = Actions.ActOnNameClassifiedAsUndeclaredNonType(II, Loc);
break;
}
default:
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
if (ParseUnqualifiedId(SS,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false,
/*ObjectType=*/nullptr, &TemplateKWLoc, Name))
return ExprError();
// This is only the direct operand of an & operator if it is not
// followed by a postfix-expression suffix.
if (isAddressOfOperand && isPostfixExpressionSuffixStart())
isAddressOfOperand = false;
E = Actions.ActOnIdExpression(
getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false,
&Replacement);
break;
}
ExprResult E = Actions.ActOnIdExpression(
getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false,
&Replacement);
if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less)) if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less))
checkPotentialAngleBracket(E); checkPotentialAngleBracket(E);
return E; return E;

View File

@ -187,7 +187,7 @@ Retry:
// Try to limit which sets of keywords should be included in typo // Try to limit which sets of keywords should be included in typo
// correction based on what the next token is. // correction based on what the next token is.
StatementFilterCCC CCC(Next); StatementFilterCCC CCC(Next);
if (TryAnnotateName(/*IsAddressOfOperand*/ false, &CCC) == ANK_Error) { if (TryAnnotateName(&CCC) == ANK_Error) {
// Handle errors here by skipping up to the next semicolon or '}', and // Handle errors here by skipping up to the next semicolon or '}', and
// eat the semicolon if that's what stopped us. // eat the semicolon if that's what stopped us.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);

View File

@ -1330,7 +1330,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
// this is ambiguous. Typo-correct to type and expression keywords and // this is ambiguous. Typo-correct to type and expression keywords and
// to types and identifiers, in order to try to recover from errors. // to types and identifiers, in order to try to recover from errors.
TentativeParseCCC CCC(Next); TentativeParseCCC CCC(Next);
switch (TryAnnotateName(false /* no nested name specifier */, &CCC)) { switch (TryAnnotateName(&CCC)) {
case ANK_Error: case ANK_Error:
return TPResult::Error; return TPResult::Error;
case ANK_TentativeDecl: case ANK_TentativeDecl:
@ -1569,7 +1569,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
} else { } else {
// Try to resolve the name. If it doesn't exist, assume it was // Try to resolve the name. If it doesn't exist, assume it was
// intended to name a type and keep disambiguating. // intended to name a type and keep disambiguating.
switch (TryAnnotateName(false /* SS is not dependent */)) { switch (TryAnnotateName()) {
case ANK_Error: case ANK_Error:
return TPResult::Error; return TPResult::Error;
case ANK_TentativeDecl: case ANK_TentativeDecl:

View File

@ -1561,13 +1561,10 @@ void Parser::AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation) {
/// with a typo-corrected keyword. This is only appropriate when the current /// with a typo-corrected keyword. This is only appropriate when the current
/// name must refer to an entity which has already been declared. /// name must refer to an entity which has already been declared.
/// ///
/// \param IsAddressOfOperand Must be \c true if the name is preceded by an '&'
/// and might possibly have a dependent nested name specifier.
/// \param CCC Indicates how to perform typo-correction for this name. If NULL, /// \param CCC Indicates how to perform typo-correction for this name. If NULL,
/// no typo correction will be performed. /// no typo correction will be performed.
Parser::AnnotatedNameKind Parser::AnnotatedNameKind
Parser::TryAnnotateName(bool IsAddressOfOperand, Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) {
CorrectionCandidateCallback *CCC) {
assert(Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope)); assert(Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope));
const bool EnteringContext = false; const bool EnteringContext = false;
@ -1603,9 +1600,8 @@ Parser::TryAnnotateName(bool IsAddressOfOperand,
// after a scope specifier, because in general we can't recover from typos // after a scope specifier, because in general we can't recover from typos
// there (eg, after correcting 'A::template B<X>::C' [sic], we would need to // there (eg, after correcting 'A::template B<X>::C' [sic], we would need to
// jump back into scope specifier parsing). // jump back into scope specifier parsing).
Sema::NameClassification Classification = Sema::NameClassification Classification = Actions.ClassifyName(
Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next, getCurScope(), SS, Name, NameLoc, Next, SS.isEmpty() ? CCC : nullptr);
IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr);
// If name lookup found nothing and we guessed that this was a template name, // If name lookup found nothing and we guessed that this was a template name,
// double-check before committing to that interpretation. C++20 requires that // double-check before committing to that interpretation. C++20 requires that
@ -1618,7 +1614,7 @@ Parser::TryAnnotateName(bool IsAddressOfOperand,
FakeNext.setKind(tok::unknown); FakeNext.setKind(tok::unknown);
Classification = Classification =
Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, FakeNext, Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, FakeNext,
IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr); SS.isEmpty() ? CCC : nullptr);
} }
switch (Classification.getKind()) { switch (Classification.getKind()) {
@ -1671,7 +1667,7 @@ Parser::TryAnnotateName(bool IsAddressOfOperand,
return ANK_Success; return ANK_Success;
} }
case Sema::NC_Expression: case Sema::NC_ContextIndependentExpr:
Tok.setKind(tok::annot_primary_expr); Tok.setKind(tok::annot_primary_expr);
setExprAnnotation(Tok, Classification.getExpression()); setExprAnnotation(Tok, Classification.getExpression());
Tok.setAnnotationEndLoc(NameLoc); Tok.setAnnotationEndLoc(NameLoc);
@ -1680,6 +1676,29 @@ Parser::TryAnnotateName(bool IsAddressOfOperand,
PP.AnnotateCachedTokens(Tok); PP.AnnotateCachedTokens(Tok);
return ANK_Success; return ANK_Success;
case Sema::NC_NonType:
Tok.setKind(tok::annot_non_type);
setNonTypeAnnotation(Tok, Classification.getNonTypeDecl());
Tok.setLocation(NameLoc);
Tok.setAnnotationEndLoc(NameLoc);
PP.AnnotateCachedTokens(Tok);
if (SS.isNotEmpty())
AnnotateScopeToken(SS, !WasScopeAnnotation);
return ANK_Success;
case Sema::NC_UndeclaredNonType:
case Sema::NC_DependentNonType:
Tok.setKind(Classification.getKind() == Sema::NC_UndeclaredNonType
? tok::annot_non_type_undeclared
: tok::annot_non_type_dependent);
setIdentifierAnnotation(Tok, Name);
Tok.setLocation(NameLoc);
Tok.setAnnotationEndLoc(NameLoc);
PP.AnnotateCachedTokens(Tok);
if (SS.isNotEmpty())
AnnotateScopeToken(SS, !WasScopeAnnotation);
return ANK_Success;
case Sema::NC_TypeTemplate: case Sema::NC_TypeTemplate:
if (Next.isNot(tok::less)) { if (Next.isNot(tok::less)) {
// This may be a type template being used as a template template argument. // This may be a type template being used as a template template argument.
@ -1701,9 +1720,6 @@ Parser::TryAnnotateName(bool IsAddressOfOperand,
return ANK_Error; return ANK_Error;
return ANK_Success; return ANK_Success;
} }
case Sema::NC_NestedNameSpecifier:
llvm_unreachable("already parsed nested name specifier");
} }
// Unable to classify the name, but maybe we can annotate a scope specifier. // Unable to classify the name, but maybe we can annotate a scope specifier.

View File

@ -845,18 +845,18 @@ static ParsedType buildNestedType(Sema &S, CXXScopeSpec &SS,
return S.CreateParsedType(T, Builder.getTypeSourceInfo(Context, T)); return S.CreateParsedType(T, Builder.getTypeSourceInfo(Context, T));
} }
Sema::NameClassification Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS,
Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, IdentifierInfo *&Name,
SourceLocation NameLoc, const Token &NextToken, SourceLocation NameLoc,
bool IsAddressOfOperand, CorrectionCandidateCallback *CCC) { const Token &NextToken,
CorrectionCandidateCallback *CCC) {
DeclarationNameInfo NameInfo(Name, NameLoc); DeclarationNameInfo NameInfo(Name, NameLoc);
ObjCMethodDecl *CurMethod = getCurMethodDecl(); ObjCMethodDecl *CurMethod = getCurMethodDecl();
if (NextToken.is(tok::coloncolon)) { assert(NextToken.isNot(tok::coloncolon) &&
NestedNameSpecInfo IdInfo(Name, NameLoc, NextToken.getLocation()); "parse nested name specifiers before calling ClassifyName");
BuildCXXNestedNameSpecifier(S, IdInfo, false, SS, nullptr, false); if (getLangOpts().CPlusPlus && SS.isSet() &&
} else if (getLangOpts().CPlusPlus && SS.isSet() && isCurrentClassName(*Name, S, &SS)) {
isCurrentClassName(*Name, S, &SS)) {
// Per [class.qual]p2, this names the constructors of SS, not the // Per [class.qual]p2, this names the constructors of SS, not the
// injected-class-name. We don't have a classification for that. // injected-class-name. We don't have a classification for that.
// There's not much point caching this result, since the parser // There's not much point caching this result, since the parser
@ -880,9 +880,15 @@ Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name,
// FIXME: This lookup really, really needs to be folded in to the normal // FIXME: This lookup really, really needs to be folded in to the normal
// unqualified lookup mechanism. // unqualified lookup mechanism.
if (!SS.isSet() && CurMethod && !isResultTypeOrTemplate(Result, NextToken)) { if (!SS.isSet() && CurMethod && !isResultTypeOrTemplate(Result, NextToken)) {
ExprResult E = LookupInObjCMethod(Result, S, Name, true); DeclResult Ivar = LookupIvarInObjCMethod(Result, S, Name);
if (E.get() || E.isInvalid()) if (Ivar.isInvalid())
return E; return NameClassification::Error();
if (Ivar.isUsable())
return NameClassification::NonType(cast<NamedDecl>(Ivar.get()));
// We defer builtin creation until after ivar lookup inside ObjC methods.
if (Result.empty())
LookupBuiltin(Result);
} }
bool SecondTry = false; bool SecondTry = false;
@ -897,7 +903,7 @@ Corrected:
// In C++, this is an ADL-only call. // In C++, this is an ADL-only call.
// FIXME: Reference? // FIXME: Reference?
if (getLangOpts().CPlusPlus) if (getLangOpts().CPlusPlus)
return BuildDeclarationNameExpr(SS, Result, /*ADL=*/true); return NameClassification::UndeclaredNonType();
// C90 6.3.2.2: // C90 6.3.2.2:
// If the expression that precedes the parenthesized argument list in a // If the expression that precedes the parenthesized argument list in a
@ -911,11 +917,8 @@ Corrected:
// appeared. // appeared.
// //
// We also allow this in C99 as an extension. // We also allow this in C99 as an extension.
if (NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *Name, S)) { if (NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *Name, S))
Result.addDecl(D); return NameClassification::NonType(D);
Result.resolveKind();
return BuildDeclarationNameExpr(SS, Result, /*ADL=*/false);
}
} }
if (getLangOpts().CPlusPlus2a && !SS.isSet() && NextToken.is(tok::less)) { if (getLangOpts().CPlusPlus2a && !SS.isSet() && NextToken.is(tok::less)) {
@ -990,9 +993,12 @@ Corrected:
// reference the ivar. // reference the ivar.
// FIXME: This is a gross hack. // FIXME: This is a gross hack.
if (ObjCIvarDecl *Ivar = Result.getAsSingle<ObjCIvarDecl>()) { if (ObjCIvarDecl *Ivar = Result.getAsSingle<ObjCIvarDecl>()) {
Result.clear(); DeclResult R =
ExprResult E(LookupInObjCMethod(Result, S, Ivar->getIdentifier())); LookupIvarInObjCMethod(Result, S, Ivar->getIdentifier());
return E; if (R.isInvalid())
return NameClassification::Error();
if (R.isUsable())
return NameClassification::NonType(Ivar);
} }
goto Corrected; goto Corrected;
@ -1018,9 +1024,7 @@ Corrected:
// perform some heroics to see if we actually have a // perform some heroics to see if we actually have a
// template-argument-list, which would indicate a missing 'template' // template-argument-list, which would indicate a missing 'template'
// keyword here. // keyword here.
return ActOnDependentIdExpression(SS, /*TemplateKWLoc=*/SourceLocation(), return NameClassification::DependentNonType();
NameInfo, IsAddressOfOperand,
/*TemplateArgs=*/nullptr);
} }
case LookupResult::Found: case LookupResult::Found:
@ -1167,9 +1171,57 @@ Corrected:
return ParsedType::make(T); return ParsedType::make(T);
} }
// FIXME: This is context-dependent. We need to defer building the member
// expression until the classification is consumed.
if (FirstDecl->isCXXClassMember()) if (FirstDecl->isCXXClassMember())
return BuildPossibleImplicitMemberExpr(SS, SourceLocation(), Result, return NameClassification::ContextIndependentExpr(
nullptr, S); BuildPossibleImplicitMemberExpr(SS, SourceLocation(), Result, nullptr,
S));
// If we already know which single declaration is referenced, just annotate
// that declaration directly.
bool ADL = UseArgumentDependentLookup(SS, Result, NextToken.is(tok::l_paren));
if (Result.isSingleResult() && !ADL)
return NameClassification::NonType(Result.getRepresentativeDecl());
// Build an UnresolvedLookupExpr. Note that this doesn't depend on the
// context in which we performed classification, so it's safe to do now.
return NameClassification::ContextIndependentExpr(
BuildDeclarationNameExpr(SS, Result, ADL));
}
ExprResult
Sema::ActOnNameClassifiedAsUndeclaredNonType(IdentifierInfo *Name,
SourceLocation NameLoc) {
assert(getLangOpts().CPlusPlus && "ADL-only call in C?");
CXXScopeSpec SS;
LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName);
return BuildDeclarationNameExpr(SS, Result, /*ADL=*/true);
}
ExprResult
Sema::ActOnNameClassifiedAsDependentNonType(const CXXScopeSpec &SS,
IdentifierInfo *Name,
SourceLocation NameLoc,
bool IsAddressOfOperand) {
DeclarationNameInfo NameInfo(Name, NameLoc);
return ActOnDependentIdExpression(SS, /*TemplateKWLoc=*/SourceLocation(),
NameInfo, IsAddressOfOperand,
/*TemplateArgs=*/nullptr);
}
ExprResult Sema::ActOnNameClassifiedAsNonType(Scope *S, const CXXScopeSpec &SS,
NamedDecl *Found,
SourceLocation NameLoc,
const Token &NextToken) {
if (getCurMethodDecl() && SS.isEmpty())
if (auto *Ivar = dyn_cast<ObjCIvarDecl>(Found->getUnderlyingDecl()))
return BuildIvarRefExpr(S, NameLoc, Ivar);
// Reconstruct the lookup result.
LookupResult Result(*this, Found->getDeclName(), NameLoc, LookupOrdinaryName);
Result.addDecl(Found);
Result.resolveKind();
bool ADL = UseArgumentDependentLookup(SS, Result, NextToken.is(tok::l_paren)); bool ADL = UseArgumentDependentLookup(SS, Result, NextToken.is(tok::l_paren));
return BuildDeclarationNameExpr(SS, Result, ADL); return BuildDeclarationNameExpr(SS, Result, ADL);

View File

@ -2482,23 +2482,20 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr(
return BuildDeclarationNameExpr(SS, R, /* ADL */ false); return BuildDeclarationNameExpr(SS, R, /* ADL */ false);
} }
/// LookupInObjCMethod - The parser has read a name in, and Sema has /// The parser has read a name in, and Sema has detected that we're currently
/// detected that we're currently inside an ObjC method. Perform some /// inside an ObjC method. Perform some additional checks and determine if we
/// additional lookup. /// should form a reference to an ivar.
/// ///
/// Ideally, most of this would be done by lookup, but there's /// Ideally, most of this would be done by lookup, but there's
/// actually quite a lot of extra work involved. /// actually quite a lot of extra work involved.
/// DeclResult Sema::LookupIvarInObjCMethod(LookupResult &Lookup, Scope *S,
/// Returns a null sentinel to indicate trivial success. IdentifierInfo *II) {
ExprResult
Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
IdentifierInfo *II, bool AllowBuiltinCreation) {
SourceLocation Loc = Lookup.getNameLoc(); SourceLocation Loc = Lookup.getNameLoc();
ObjCMethodDecl *CurMethod = getCurMethodDecl(); ObjCMethodDecl *CurMethod = getCurMethodDecl();
// Check for error condition which is already reported. // Check for error condition which is already reported.
if (!CurMethod) if (!CurMethod)
return ExprError(); return DeclResult(true);
// There are two cases to handle here. 1) scoped lookup could have failed, // There are two cases to handle here. 1) scoped lookup could have failed,
// in which case we should look for an ivar. 2) scoped lookup could have // in which case we should look for an ivar. 2) scoped lookup could have
@ -2526,18 +2523,10 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
ObjCIvarDecl *IV = nullptr; ObjCIvarDecl *IV = nullptr;
if (IFace && (IV = IFace->lookupInstanceVariable(II, ClassDeclared))) { if (IFace && (IV = IFace->lookupInstanceVariable(II, ClassDeclared))) {
// Diagnose using an ivar in a class method. // Diagnose using an ivar in a class method.
if (IsClassMethod) if (IsClassMethod) {
return ExprError(Diag(Loc, diag::err_ivar_use_in_class_method) Diag(Loc, diag::err_ivar_use_in_class_method) << IV->getDeclName();
<< IV->getDeclName()); return DeclResult(true);
}
// If we're referencing an invalid decl, just return this as a silent
// error node. The error diagnostic was already emitted on the decl.
if (IV->isInvalidDecl())
return ExprError();
// Check if referencing a field with __attribute__((deprecated)).
if (DiagnoseUseOfDecl(IV, Loc))
return ExprError();
// Diagnose the use of an ivar outside of the declaring class. // Diagnose the use of an ivar outside of the declaring class.
if (IV->getAccessControl() == ObjCIvarDecl::Private && if (IV->getAccessControl() == ObjCIvarDecl::Private &&
@ -2545,46 +2534,8 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
!getLangOpts().DebuggerSupport) !getLangOpts().DebuggerSupport)
Diag(Loc, diag::err_private_ivar_access) << IV->getDeclName(); Diag(Loc, diag::err_private_ivar_access) << IV->getDeclName();
// FIXME: This should use a new expr for a direct reference, don't // Success.
// turn this into Self->ivar, just return a BareIVarExpr or something. return IV;
IdentifierInfo &II = Context.Idents.get("self");
UnqualifiedId SelfName;
SelfName.setIdentifier(&II, SourceLocation());
SelfName.setKind(UnqualifiedIdKind::IK_ImplicitSelfParam);
CXXScopeSpec SelfScopeSpec;
SourceLocation TemplateKWLoc;
ExprResult SelfExpr =
ActOnIdExpression(S, SelfScopeSpec, TemplateKWLoc, SelfName,
/*HasTrailingLParen=*/false,
/*IsAddressOfOperand=*/false);
if (SelfExpr.isInvalid())
return ExprError();
SelfExpr = DefaultLvalueConversion(SelfExpr.get());
if (SelfExpr.isInvalid())
return ExprError();
MarkAnyDeclReferenced(Loc, IV, true);
ObjCMethodFamily MF = CurMethod->getMethodFamily();
if (MF != OMF_init && MF != OMF_dealloc && MF != OMF_finalize &&
!IvarBacksCurrentMethodAccessor(IFace, CurMethod, IV))
Diag(Loc, diag::warn_direct_ivar_access) << IV->getDeclName();
ObjCIvarRefExpr *Result = new (Context)
ObjCIvarRefExpr(IV, IV->getUsageType(SelfExpr.get()->getType()), Loc,
IV->getLocation(), SelfExpr.get(), true, true);
if (IV->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
if (!isUnevaluatedContext() &&
!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, Loc))
getCurFunction()->recordUseOfWeak(Result);
}
if (getLangOpts().ObjCAutoRefCount)
if (const BlockDecl *BD = CurContext->getInnermostBlockDecl())
ImplicitlyRetainedSelfLocs.push_back({Loc, BD});
return Result;
} }
} else if (CurMethod->isInstanceMethod()) { } else if (CurMethod->isInstanceMethod()) {
// We should warn if a local variable hides an ivar. // We should warn if a local variable hides an ivar.
@ -2599,25 +2550,97 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
} else if (Lookup.isSingleResult() && } else if (Lookup.isSingleResult() &&
Lookup.getFoundDecl()->isDefinedOutsideFunctionOrMethod()) { Lookup.getFoundDecl()->isDefinedOutsideFunctionOrMethod()) {
// If accessing a stand-alone ivar in a class method, this is an error. // If accessing a stand-alone ivar in a class method, this is an error.
if (const ObjCIvarDecl *IV = dyn_cast<ObjCIvarDecl>(Lookup.getFoundDecl())) if (const ObjCIvarDecl *IV =
return ExprError(Diag(Loc, diag::err_ivar_use_in_class_method) dyn_cast<ObjCIvarDecl>(Lookup.getFoundDecl())) {
<< IV->getDeclName()); Diag(Loc, diag::err_ivar_use_in_class_method) << IV->getDeclName();
} return DeclResult(true);
if (Lookup.empty() && II && AllowBuiltinCreation) {
// FIXME. Consolidate this with similar code in LookupName.
if (unsigned BuiltinID = II->getBuiltinID()) {
if (!(getLangOpts().CPlusPlus &&
Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID))) {
NamedDecl *D = LazilyCreateBuiltin((IdentifierInfo *)II, BuiltinID,
S, Lookup.isForRedeclaration(),
Lookup.getNameLoc());
if (D) Lookup.addDecl(D);
}
} }
} }
// Didn't encounter an error, didn't find an ivar.
return DeclResult(false);
}
ExprResult Sema::BuildIvarRefExpr(Scope *S, SourceLocation Loc,
ObjCIvarDecl *IV) {
ObjCMethodDecl *CurMethod = getCurMethodDecl();
assert(CurMethod && CurMethod->isInstanceMethod() &&
"should not reference ivar from this context");
ObjCInterfaceDecl *IFace = CurMethod->getClassInterface();
assert(IFace && "should not reference ivar from this context");
// If we're referencing an invalid decl, just return this as a silent
// error node. The error diagnostic was already emitted on the decl.
if (IV->isInvalidDecl())
return ExprError();
// Check if referencing a field with __attribute__((deprecated)).
if (DiagnoseUseOfDecl(IV, Loc))
return ExprError();
// FIXME: This should use a new expr for a direct reference, don't
// turn this into Self->ivar, just return a BareIVarExpr or something.
IdentifierInfo &II = Context.Idents.get("self");
UnqualifiedId SelfName;
SelfName.setIdentifier(&II, SourceLocation());
SelfName.setKind(UnqualifiedIdKind::IK_ImplicitSelfParam);
CXXScopeSpec SelfScopeSpec;
SourceLocation TemplateKWLoc;
ExprResult SelfExpr =
ActOnIdExpression(S, SelfScopeSpec, TemplateKWLoc, SelfName,
/*HasTrailingLParen=*/false,
/*IsAddressOfOperand=*/false);
if (SelfExpr.isInvalid())
return ExprError();
SelfExpr = DefaultLvalueConversion(SelfExpr.get());
if (SelfExpr.isInvalid())
return ExprError();
MarkAnyDeclReferenced(Loc, IV, true);
ObjCMethodFamily MF = CurMethod->getMethodFamily();
if (MF != OMF_init && MF != OMF_dealloc && MF != OMF_finalize &&
!IvarBacksCurrentMethodAccessor(IFace, CurMethod, IV))
Diag(Loc, diag::warn_direct_ivar_access) << IV->getDeclName();
ObjCIvarRefExpr *Result = new (Context)
ObjCIvarRefExpr(IV, IV->getUsageType(SelfExpr.get()->getType()), Loc,
IV->getLocation(), SelfExpr.get(), true, true);
if (IV->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
if (!isUnevaluatedContext() &&
!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, Loc))
getCurFunction()->recordUseOfWeak(Result);
}
if (getLangOpts().ObjCAutoRefCount)
if (const BlockDecl *BD = CurContext->getInnermostBlockDecl())
ImplicitlyRetainedSelfLocs.push_back({Loc, BD});
return Result;
}
/// The parser has read a name in, and Sema has detected that we're currently
/// inside an ObjC method. Perform some additional checks and determine if we
/// should form a reference to an ivar. If so, build an expression referencing
/// that ivar.
ExprResult
Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S,
IdentifierInfo *II, bool AllowBuiltinCreation) {
// FIXME: Integrate this lookup step into LookupParsedName.
DeclResult Ivar = LookupIvarInObjCMethod(Lookup, S, II);
if (Ivar.isInvalid())
return ExprError();
if (Ivar.isUsable())
return BuildIvarRefExpr(S, Lookup.getNameLoc(),
cast<ObjCIvarDecl>(Ivar.get()));
if (Lookup.empty() && II && AllowBuiltinCreation)
LookupBuiltin(Lookup);
// Sentinel value saying that we didn't do anything special. // Sentinel value saying that we didn't do anything special.
return ExprResult((Expr *)nullptr); return ExprResult(false);
} }
/// Cast a base object to a member's actual type. /// Cast a base object to a member's actual type.

View File

@ -826,7 +826,7 @@ static void InsertOCLBuiltinDeclarationsFromTable(Sema &S, LookupResult &LR,
/// Lookup a builtin function, when name lookup would otherwise /// Lookup a builtin function, when name lookup would otherwise
/// fail. /// fail.
static bool LookupBuiltin(Sema &S, LookupResult &R) { bool Sema::LookupBuiltin(LookupResult &R) {
Sema::LookupNameKind NameKind = R.getLookupKind(); Sema::LookupNameKind NameKind = R.getLookupKind();
// If we didn't find a use of this identifier, and if the identifier // If we didn't find a use of this identifier, and if the identifier
@ -836,21 +836,21 @@ static bool LookupBuiltin(Sema &S, LookupResult &R) {
NameKind == Sema::LookupRedeclarationWithLinkage) { NameKind == Sema::LookupRedeclarationWithLinkage) {
IdentifierInfo *II = R.getLookupName().getAsIdentifierInfo(); IdentifierInfo *II = R.getLookupName().getAsIdentifierInfo();
if (II) { if (II) {
if (S.getLangOpts().CPlusPlus && NameKind == Sema::LookupOrdinaryName) { if (getLangOpts().CPlusPlus && NameKind == Sema::LookupOrdinaryName) {
if (II == S.getASTContext().getMakeIntegerSeqName()) { if (II == getASTContext().getMakeIntegerSeqName()) {
R.addDecl(S.getASTContext().getMakeIntegerSeqDecl()); R.addDecl(getASTContext().getMakeIntegerSeqDecl());
return true; return true;
} else if (II == S.getASTContext().getTypePackElementName()) { } else if (II == getASTContext().getTypePackElementName()) {
R.addDecl(S.getASTContext().getTypePackElementDecl()); R.addDecl(getASTContext().getTypePackElementDecl());
return true; return true;
} }
} }
// Check if this is an OpenCL Builtin, and if so, insert its overloads. // Check if this is an OpenCL Builtin, and if so, insert its overloads.
if (S.getLangOpts().OpenCL && S.getLangOpts().DeclareOpenCLBuiltins) { if (getLangOpts().OpenCL && getLangOpts().DeclareOpenCLBuiltins) {
auto Index = isOpenCLBuiltin(II->getName()); auto Index = isOpenCLBuiltin(II->getName());
if (Index.first) { if (Index.first) {
InsertOCLBuiltinDeclarationsFromTable(S, R, II, Index.first - 1, InsertOCLBuiltinDeclarationsFromTable(*this, R, II, Index.first - 1,
Index.second); Index.second);
return true; return true;
} }
@ -860,14 +860,14 @@ static bool LookupBuiltin(Sema &S, LookupResult &R) {
if (unsigned BuiltinID = II->getBuiltinID()) { if (unsigned BuiltinID = II->getBuiltinID()) {
// In C++ and OpenCL (spec v1.2 s6.9.f), we don't have any predefined // In C++ and OpenCL (spec v1.2 s6.9.f), we don't have any predefined
// library functions like 'malloc'. Instead, we'll just error. // library functions like 'malloc'. Instead, we'll just error.
if ((S.getLangOpts().CPlusPlus || S.getLangOpts().OpenCL) && if ((getLangOpts().CPlusPlus || getLangOpts().OpenCL) &&
S.Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID))
return false; return false;
if (NamedDecl *D = S.LazilyCreateBuiltin((IdentifierInfo *)II, if (NamedDecl *D = LazilyCreateBuiltin((IdentifierInfo *)II,
BuiltinID, S.TUScope, BuiltinID, TUScope,
R.isForRedeclaration(), R.isForRedeclaration(),
R.getNameLoc())) { R.getNameLoc())) {
R.addDecl(D); R.addDecl(D);
return true; return true;
} }
@ -1013,7 +1013,7 @@ static bool LookupDirect(Sema &S, LookupResult &R, const DeclContext *DC) {
} }
} }
if (!Found && DC->isTranslationUnit() && LookupBuiltin(S, R)) if (!Found && DC->isTranslationUnit() && S.LookupBuiltin(R))
return true; return true;
if (R.getLookupName().getNameKind() if (R.getLookupName().getNameKind()
@ -2011,7 +2011,7 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
// If we didn't find a use of this identifier, and if the identifier // If we didn't find a use of this identifier, and if the identifier
// corresponds to a compiler builtin, create the decl object for the builtin // corresponds to a compiler builtin, create the decl object for the builtin
// now, injecting it into translation unit scope, and return it. // now, injecting it into translation unit scope, and return it.
if (AllowBuiltinCreation && LookupBuiltin(*this, R)) if (AllowBuiltinCreation && LookupBuiltin(R))
return true; return true;
// If we didn't find a use of this identifier, the ExternalSource // If we didn't find a use of this identifier, the ExternalSource

View File

@ -0,0 +1,10 @@
// RUN: %clang_cc1 -emit-llvm-only %s
namespace PR43080 {
int f(int i) { return sizeof i<i; }
}
namespace PR42861 {
const unsigned long s = alignof(int);
void foo() { alignas(s) int j; }
}

View File

@ -16,3 +16,10 @@ void captures_invalid_array_type() {
auto q = [child]{}; auto q = [child]{};
const int n = sizeof(q); const int n = sizeof(q);
} }
int pr43080(int i) { // expected-note {{declared here}}
return [] { // expected-note {{begins here}}
return sizeof i <
i; // expected-error {{variable 'i' cannot be implicitly captured in a lambda with no capture-default specified}}
}();
}