diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -728,6 +728,11 @@ ANNOTATION(template_id) // annotation for a C++ template-id that names a // function template specialization (not a type), // e.g., "std::swap" +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(decltype) // annotation for a decltype expression, // e.g., "decltype(foo.bar())" diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -766,6 +766,22 @@ Tok.setAnnotationValue(T.getAsOpaquePtr()); } + static NamedDecl *getNonTypeAnnotation(const Token &Tok) { + return static_cast(Tok.getAnnotationValue()); + } + + static void setNonTypeAnnotation(Token &Tok, NamedDecl *ND) { + Tok.setAnnotationValue(ND); + } + + static IdentifierInfo *getIdentifierAnnotation(const Token &Tok) { + return static_cast(Tok.getAnnotationValue()); + } + + static void setIdentifierAnnotation(Token &Tok, IdentifierInfo *ND) { + Tok.setAnnotationValue(ND); + } + /// Read an already-translated primary expression out of an annotation /// token. static ExprResult getExprAnnotation(const Token &Tok) { @@ -799,8 +815,7 @@ /// Annotation was successful. ANK_Success }; - AnnotatedNameKind TryAnnotateName(bool IsAddressOfOperand, - CorrectionCandidateCallback *CCC = nullptr); + AnnotatedNameKind TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr); /// Push a tok::annot_cxxscope token onto the token stream. void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1855,29 +1855,52 @@ /// Describes the result of the name lookup and resolution performed /// by \c ClassifyName(). enum NameClassificationKind { + /// This name is not a type or template in this context, but might be + /// something else. NC_Unknown, + /// Classification failed; an error has been produced. NC_Error, + /// The name has been typo-corrected to a keyword. NC_Keyword, + /// The name was classified as a type. NC_Type, - NC_Expression, - NC_NestedNameSpecifier, + /// The name was classified as a specific non-type, non-template + /// 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, + /// The name was classified as a variable template name. NC_VarTemplate, + /// The name was classified as a function template name. NC_FunctionTemplate, + /// The name was classified as an ADL-only function template name. NC_UndeclaredTemplate, }; class NameClassification { NameClassificationKind Kind; - ExprResult Expr; - TemplateName Template; - ParsedType Type; + union { + ExprResult Expr; + NamedDecl *NonTypeDecl; + TemplateName Template; + ParsedType Type; + }; explicit NameClassification(NameClassificationKind Kind) : Kind(Kind) {} public: - NameClassification(ExprResult Expr) : Kind(NC_Expression), Expr(Expr) {} - NameClassification(ParsedType Type) : Kind(NC_Type), Type(Type) {} NameClassification(const IdentifierInfo *Keyword) : Kind(NC_Keyword) {} @@ -1890,8 +1913,24 @@ return NameClassification(NC_Unknown); } - static NameClassification NestedNameSpecifier() { - return NameClassification(NC_NestedNameSpecifier); + static NameClassification ContextIndependentExpr(ExprResult E) { + 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) { @@ -1920,14 +1959,19 @@ NameClassificationKind getKind() const { return Kind; } + ExprResult getExpression() const { + assert(Kind == NC_ContextIndependentExpr); + return Expr; + } + ParsedType getType() const { assert(Kind == NC_Type); return Type; } - ExprResult getExpression() const { - assert(Kind == NC_Expression); - return Expr; + NamedDecl *getNonTypeDecl() const { + assert(Kind == NC_NonType); + return NonTypeDecl; } TemplateName getTemplateName() const { @@ -1971,17 +2015,29 @@ /// \param NextToken The token following the identifier. Used to help /// 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. NameClassification ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, SourceLocation NameLoc, const Token &NextToken, - bool IsAddressOfOperand, 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. enum class TemplateNameKindForDiagnostics { ClassTemplate, @@ -3407,6 +3463,7 @@ LookupNameKind NameKind, RedeclarationKind Redecl = NotForRedeclaration); + bool LookupBuiltin(LookupResult &R); bool LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation = false); bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, @@ -4389,6 +4446,10 @@ TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr, ArrayRef 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, IdentifierInfo *II, bool AllowBuiltinCreation=false); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2929,28 +2929,29 @@ IdentifierInfo *Name = AfterScope.getIdentifierInfo(); Sema::NameClassification Classification = Actions.ClassifyName( getCurScope(), SS, Name, AfterScope.getLocation(), Next, - /*IsAddressOfOperand=*/false, /*CCC=*/nullptr); + /*CCC=*/nullptr); switch (Classification.getKind()) { case Sema::NC_Error: SkipMalformedDecl(); return true; case Sema::NC_Keyword: - case Sema::NC_NestedNameSpecifier: - llvm_unreachable("typo correction and nested name specifiers not " - "possible here"); + llvm_unreachable("typo correction is not possible here"); case Sema::NC_Type: case Sema::NC_TypeTemplate: + case Sema::NC_UndeclaredNonType: + case Sema::NC_UndeclaredTemplate: // Not a previously-declared non-type entity. MightBeDeclarator = false; break; 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_FunctionTemplate: - case Sema::NC_UndeclaredTemplate: // Might be a redeclaration of a prior entity. break; } diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -840,13 +840,23 @@ return Actions.ActOnCXXNullPtrLiteral(ConsumeToken()); case tok::annot_primary_expr: - assert(Res.get() == nullptr && "Stray primary-expression annotation?"); Res = getExprAnnotation(Tok); ConsumeAnnotationToken(); if (!Res.isInvalid() && Tok.is(tok::less)) checkPotentialAngleBracket(Res); 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_decltype: // Annotate the token and tail recurse. diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -555,27 +555,66 @@ return false; } -ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand, +ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, + bool isAddressOfOperand, Token &Replacement) { - SourceLocation TemplateKWLoc; - UnqualifiedId Name; - if (ParseUnqualifiedId(SS, - /*EnteringContext=*/false, - /*AllowDestructorName=*/false, - /*AllowConstructorName=*/false, - /*AllowDeductionGuide=*/false, - /*ObjectType=*/nullptr, &TemplateKWLoc, Name)) - return ExprError(); + ExprResult E; + + // We may have already annotated this id-expression. + switch (Tok.getKind()) { + case tok::annot_non_type: { + 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; + } - // This is only the direct operand of an & operator if it is not - // followed by a postfix-expression suffix. - if (isAddressOfOperand && isPostfixExpressionSuffixStart()) - isAddressOfOperand = false; + 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)) checkPotentialAngleBracket(E); return E; diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -187,7 +187,7 @@ // Try to limit which sets of keywords should be included in typo // correction based on what the next token is. 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 // eat the semicolon if that's what stopped us. SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1330,7 +1330,7 @@ // this is ambiguous. Typo-correct to type and expression keywords and // to types and identifiers, in order to try to recover from errors. TentativeParseCCC CCC(Next); - switch (TryAnnotateName(false /* no nested name specifier */, &CCC)) { + switch (TryAnnotateName(&CCC)) { case ANK_Error: return TPResult::Error; case ANK_TentativeDecl: @@ -1569,7 +1569,7 @@ } else { // Try to resolve the name. If it doesn't exist, assume it was // intended to name a type and keep disambiguating. - switch (TryAnnotateName(false /* SS is not dependent */)) { + switch (TryAnnotateName()) { case ANK_Error: return TPResult::Error; case ANK_TentativeDecl: diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1561,13 +1561,10 @@ /// with a typo-corrected keyword. This is only appropriate when the current /// 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, /// no typo correction will be performed. Parser::AnnotatedNameKind -Parser::TryAnnotateName(bool IsAddressOfOperand, - CorrectionCandidateCallback *CCC) { +Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) { assert(Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope)); const bool EnteringContext = false; @@ -1603,9 +1600,8 @@ // after a scope specifier, because in general we can't recover from typos // there (eg, after correcting 'A::template B::C' [sic], we would need to // jump back into scope specifier parsing). - Sema::NameClassification Classification = - Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next, - IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr); + Sema::NameClassification Classification = Actions.ClassifyName( + getCurScope(), SS, Name, NameLoc, Next, SS.isEmpty() ? CCC : nullptr); // 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 @@ -1618,7 +1614,7 @@ FakeNext.setKind(tok::unknown); Classification = Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, FakeNext, - IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr); + SS.isEmpty() ? CCC : nullptr); } switch (Classification.getKind()) { @@ -1671,7 +1667,7 @@ return ANK_Success; } - case Sema::NC_Expression: + case Sema::NC_ContextIndependentExpr: Tok.setKind(tok::annot_primary_expr); setExprAnnotation(Tok, Classification.getExpression()); Tok.setAnnotationEndLoc(NameLoc); @@ -1680,6 +1676,29 @@ PP.AnnotateCachedTokens(Tok); 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: if (Next.isNot(tok::less)) { // This may be a type template being used as a template template argument. @@ -1701,9 +1720,6 @@ return ANK_Error; 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. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -845,18 +845,18 @@ return S.CreateParsedType(T, Builder.getTypeSourceInfo(Context, T)); } -Sema::NameClassification -Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, - SourceLocation NameLoc, const Token &NextToken, - bool IsAddressOfOperand, CorrectionCandidateCallback *CCC) { +Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, + IdentifierInfo *&Name, + SourceLocation NameLoc, + const Token &NextToken, + CorrectionCandidateCallback *CCC) { DeclarationNameInfo NameInfo(Name, NameLoc); ObjCMethodDecl *CurMethod = getCurMethodDecl(); - if (NextToken.is(tok::coloncolon)) { - NestedNameSpecInfo IdInfo(Name, NameLoc, NextToken.getLocation()); - BuildCXXNestedNameSpecifier(S, IdInfo, false, SS, nullptr, false); - } else if (getLangOpts().CPlusPlus && SS.isSet() && - isCurrentClassName(*Name, S, &SS)) { + assert(NextToken.isNot(tok::coloncolon) && + "parse nested name specifiers before calling ClassifyName"); + if (getLangOpts().CPlusPlus && SS.isSet() && + isCurrentClassName(*Name, S, &SS)) { // Per [class.qual]p2, this names the constructors of SS, not the // injected-class-name. We don't have a classification for that. // There's not much point caching this result, since the parser @@ -880,9 +880,15 @@ // FIXME: This lookup really, really needs to be folded in to the normal // unqualified lookup mechanism. if (!SS.isSet() && CurMethod && !isResultTypeOrTemplate(Result, NextToken)) { - ExprResult E = LookupInObjCMethod(Result, S, Name, true); - if (E.get() || E.isInvalid()) - return E; + DeclResult Ivar = LookupIvarInObjCMethod(Result, S, Name); + if (Ivar.isInvalid()) + return NameClassification::Error(); + if (Ivar.isUsable()) + return NameClassification::NonType(cast(Ivar.get())); + + // We defer builtin creation until after ivar lookup inside ObjC methods. + if (Result.empty()) + LookupBuiltin(Result); } bool SecondTry = false; @@ -897,7 +903,7 @@ // In C++, this is an ADL-only call. // FIXME: Reference? if (getLangOpts().CPlusPlus) - return BuildDeclarationNameExpr(SS, Result, /*ADL=*/true); + return NameClassification::UndeclaredNonType(); // C90 6.3.2.2: // If the expression that precedes the parenthesized argument list in a @@ -911,11 +917,8 @@ // appeared. // // We also allow this in C99 as an extension. - if (NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *Name, S)) { - Result.addDecl(D); - Result.resolveKind(); - return BuildDeclarationNameExpr(SS, Result, /*ADL=*/false); - } + if (NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *Name, S)) + return NameClassification::NonType(D); } if (getLangOpts().CPlusPlus2a && !SS.isSet() && NextToken.is(tok::less)) { @@ -990,9 +993,12 @@ // reference the ivar. // FIXME: This is a gross hack. if (ObjCIvarDecl *Ivar = Result.getAsSingle()) { - Result.clear(); - ExprResult E(LookupInObjCMethod(Result, S, Ivar->getIdentifier())); - return E; + DeclResult R = + LookupIvarInObjCMethod(Result, S, Ivar->getIdentifier()); + if (R.isInvalid()) + return NameClassification::Error(); + if (R.isUsable()) + return NameClassification::NonType(Ivar); } goto Corrected; @@ -1018,9 +1024,7 @@ // perform some heroics to see if we actually have a // template-argument-list, which would indicate a missing 'template' // keyword here. - return ActOnDependentIdExpression(SS, /*TemplateKWLoc=*/SourceLocation(), - NameInfo, IsAddressOfOperand, - /*TemplateArgs=*/nullptr); + return NameClassification::DependentNonType(); } case LookupResult::Found: @@ -1167,9 +1171,57 @@ 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()) - return BuildPossibleImplicitMemberExpr(SS, SourceLocation(), Result, - nullptr, S); + return NameClassification::ContextIndependentExpr( + 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(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)); return BuildDeclarationNameExpr(SS, Result, ADL); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2482,23 +2482,20 @@ return BuildDeclarationNameExpr(SS, R, /* ADL */ false); } -/// LookupInObjCMethod - The parser has read a name in, and Sema has -/// detected that we're currently inside an ObjC method. Perform some -/// additional lookup. +/// 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. /// /// Ideally, most of this would be done by lookup, but there's /// actually quite a lot of extra work involved. -/// -/// Returns a null sentinel to indicate trivial success. -ExprResult -Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S, - IdentifierInfo *II, bool AllowBuiltinCreation) { +DeclResult Sema::LookupIvarInObjCMethod(LookupResult &Lookup, Scope *S, + IdentifierInfo *II) { SourceLocation Loc = Lookup.getNameLoc(); ObjCMethodDecl *CurMethod = getCurMethodDecl(); // Check for error condition which is already reported. if (!CurMethod) - return ExprError(); + return DeclResult(true); // 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 @@ -2526,18 +2523,10 @@ ObjCIvarDecl *IV = nullptr; if (IFace && (IV = IFace->lookupInstanceVariable(II, ClassDeclared))) { // Diagnose using an ivar in a class method. - if (IsClassMethod) - return ExprError(Diag(Loc, diag::err_ivar_use_in_class_method) - << IV->getDeclName()); - - // 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(); + if (IsClassMethod) { + Diag(Loc, diag::err_ivar_use_in_class_method) << IV->getDeclName(); + return DeclResult(true); + } // Diagnose the use of an ivar outside of the declaring class. if (IV->getAccessControl() == ObjCIvarDecl::Private && @@ -2545,46 +2534,8 @@ !getLangOpts().DebuggerSupport) Diag(Loc, diag::err_private_ivar_access) << IV->getDeclName(); - // 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; + // Success. + return IV; } } else if (CurMethod->isInstanceMethod()) { // We should warn if a local variable hides an ivar. @@ -2599,25 +2550,97 @@ } else if (Lookup.isSingleResult() && Lookup.getFoundDecl()->isDefinedOutsideFunctionOrMethod()) { // If accessing a stand-alone ivar in a class method, this is an error. - if (const ObjCIvarDecl *IV = dyn_cast(Lookup.getFoundDecl())) - return ExprError(Diag(Loc, diag::err_ivar_use_in_class_method) - << IV->getDeclName()); - } - - 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); - } + if (const ObjCIvarDecl *IV = + dyn_cast(Lookup.getFoundDecl())) { + Diag(Loc, diag::err_ivar_use_in_class_method) << IV->getDeclName(); + return DeclResult(true); } } + + // 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(Ivar.get())); + + if (Lookup.empty() && II && AllowBuiltinCreation) + LookupBuiltin(Lookup); + // 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. diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -826,7 +826,7 @@ /// Lookup a builtin function, when name lookup would otherwise /// fail. -static bool LookupBuiltin(Sema &S, LookupResult &R) { +bool Sema::LookupBuiltin(LookupResult &R) { Sema::LookupNameKind NameKind = R.getLookupKind(); // If we didn't find a use of this identifier, and if the identifier @@ -836,21 +836,21 @@ NameKind == Sema::LookupRedeclarationWithLinkage) { IdentifierInfo *II = R.getLookupName().getAsIdentifierInfo(); if (II) { - if (S.getLangOpts().CPlusPlus && NameKind == Sema::LookupOrdinaryName) { - if (II == S.getASTContext().getMakeIntegerSeqName()) { - R.addDecl(S.getASTContext().getMakeIntegerSeqDecl()); + if (getLangOpts().CPlusPlus && NameKind == Sema::LookupOrdinaryName) { + if (II == getASTContext().getMakeIntegerSeqName()) { + R.addDecl(getASTContext().getMakeIntegerSeqDecl()); return true; - } else if (II == S.getASTContext().getTypePackElementName()) { - R.addDecl(S.getASTContext().getTypePackElementDecl()); + } else if (II == getASTContext().getTypePackElementName()) { + R.addDecl(getASTContext().getTypePackElementDecl()); return true; } } // 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()); if (Index.first) { - InsertOCLBuiltinDeclarationsFromTable(S, R, II, Index.first - 1, + InsertOCLBuiltinDeclarationsFromTable(*this, R, II, Index.first - 1, Index.second); return true; } @@ -860,14 +860,14 @@ if (unsigned BuiltinID = II->getBuiltinID()) { // 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. - if ((S.getLangOpts().CPlusPlus || S.getLangOpts().OpenCL) && - S.Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) + if ((getLangOpts().CPlusPlus || getLangOpts().OpenCL) && + Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) return false; - if (NamedDecl *D = S.LazilyCreateBuiltin((IdentifierInfo *)II, - BuiltinID, S.TUScope, - R.isForRedeclaration(), - R.getNameLoc())) { + if (NamedDecl *D = LazilyCreateBuiltin((IdentifierInfo *)II, + BuiltinID, TUScope, + R.isForRedeclaration(), + R.getNameLoc())) { R.addDecl(D); return true; } @@ -1013,7 +1013,7 @@ } } - if (!Found && DC->isTranslationUnit() && LookupBuiltin(S, R)) + if (!Found && DC->isTranslationUnit() && S.LookupBuiltin(R)) return true; if (R.getLookupName().getNameKind() @@ -2011,7 +2011,7 @@ // 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 // now, injecting it into translation unit scope, and return it. - if (AllowBuiltinCreation && LookupBuiltin(*this, R)) + if (AllowBuiltinCreation && LookupBuiltin(R)) return true; // If we didn't find a use of this identifier, the ExternalSource diff --git a/clang/test/CodeGenCXX/odr-use-lookahead.cpp b/clang/test/CodeGenCXX/odr-use-lookahead.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/odr-use-lookahead.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -emit-llvm-only %s + +namespace PR43080 { + int f(int i) { return sizeof i