Author: Krystian Stasiowski Date: 2024-05-20T13:55:01-04:00 New Revision: fd87d765c0455265aea4595a3741a96b4c078fbc
URL: https://github.com/llvm/llvm-project/commit/fd87d765c0455265aea4595a3741a96b4c078fbc DIFF: https://github.com/llvm/llvm-project/commit/fd87d765c0455265aea4595a3741a96b4c078fbc.diff LOG: [Clang][Sema] Don't build CXXDependentScopeMemberExprs for potentially implicit class member access expressions (#92318) According to [expr.prim.id.general] p2: > If an _id-expression_ `E` denotes a non-static non-type member of some class `C` at a point where the current class is `X` and > - `E` is potentially evaluated or `C` is `X` or a base class of `X`, and > - `E` is not the _id-expression_ of a class member access expression, and > - if `E` is a _qualified-id_, `E` is not the un-parenthesized operand of the unary `&` operator, > > the _id-expression_ is transformed into a class member access expression using `(*this)` as the object expression. Consider the following: ``` struct A { void f0(); template<typename T> void f1(); }; template<typename T> struct B : T { auto g0() -> decltype(T::f0()); // ok auto g1() -> decltype(T::template f1<int>()); // error: call to non-static member function without an object argument }; template struct B<A>; ``` Clang incorrectly rejects the call to `f1` in the _trailing-return-type_ of `g1`. Furthermore, the following snippet results in a crash during codegen: ``` struct A { void f(); }; template<typename T> struct B : T { template<typename U> static void g(); template<> void g<int>() { return T::f(); // crash here } }; template struct B<A>; ``` This happens because we unconditionally build a `CXXDependentScopeMemberExpr` (with an implicit object expression) for `T::f` when parsing the template definition, even though we don't know whether `g` is an implicit object member function yet. This patch fixes these issues by instead building `DependentScopeDeclRefExpr`s for such expressions, and only transforming them into implicit class member access expressions during instantiation. Since we implemented the MS "unqualified lookup into dependent bases" extension by building an implicit class member access (and relying on the first component name of the _nested-name-specifier_ to be looked up in the context of the object expression during instantiation), we instead pre-append a fake _nested-name-specifier_ that refers to the injected-class-name of the enclosing class. This patch also refactors `Sema::BuildQualifiedDeclarationNameExpr` and `Sema::BuildQualifiedTemplateIdExpr`, streamlining their implementation and removing any redundant checks. Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaCXXScopeSpec.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaLookup.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/TreeTransform.h clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp clang/test/SemaTemplate/ms-function-specialization-class-scope.cpp clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a89e10524aa1f..ba4637d98197f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -744,6 +744,8 @@ Bug Fixes to C++ Support explicit object argument member functions. Fixes (#GH92188). - Fix a C++11 crash when a non-const non-static member function is defined out-of-line with the ``constexpr`` specifier. Fixes (#GH61004). +- Clang no longer transforms dependent qualified names into implicit class member access expressions + until it can be determined whether the name is that of a non-static member. Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6c89d275215de..5894239664c15 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5375,11 +5375,9 @@ class Sema final : public SemaBase { bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R, bool HasTrailingLParen); - ExprResult - BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS, - const DeclarationNameInfo &NameInfo, - bool IsAddressOfOperand, const Scope *S, - TypeSourceInfo **RecoveryTSI = nullptr); + ExprResult BuildQualifiedDeclarationNameExpr( + CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, + bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI = nullptr); ExprResult BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R, bool NeedsADL, @@ -8991,7 +8989,8 @@ class Sema final : public SemaBase { ExprResult BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs); + const TemplateArgumentListInfo *TemplateArgs, + bool IsAddressOfOperand); TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index fca5bd131bbc0..c405fbc0aa421 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -796,6 +796,14 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, Diag(IdInfo.IdentifierLoc, diag::ext_undeclared_unqual_id_with_dependent_base) << IdInfo.Identifier << ContainingClass; + // Fake up a nested-name-specifier that starts with the + // injected-class-name of the enclosing class. + QualType T = Context.getTypeDeclType(ContainingClass); + TypeLocBuilder TLB; + TLB.pushTrivial(Context, T, IdInfo.IdentifierLoc); + SS.Extend(Context, /*TemplateKWLoc=*/SourceLocation(), + TLB.getTypeLocInContext(Context, T), IdInfo.IdentifierLoc); + // Add the identifier to form a dependent name. SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc); return false; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index f2d0a93d9a1e7..e7731e389c1ba 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2718,34 +2718,6 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, return ExprError(); } - // C++ [temp.dep.expr]p3: - // An id-expression is type-dependent if it contains: - // -- an identifier that was declared with a dependent type, - // (note: handled after lookup) - // -- a template-id that is dependent, - // (note: handled in BuildTemplateIdExpr) - // -- a conversion-function-id that specifies a dependent type, - // -- a nested-name-specifier that contains a class-name that - // names a dependent type. - // Determine whether this is a member of an unknown specialization; - // we need to handle these diff erently. - bool DependentID = false; - if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && - Name.getCXXNameType()->isDependentType()) { - DependentID = true; - } else if (SS.isSet()) { - if (DeclContext *DC = computeDeclContext(SS, false)) { - if (RequireCompleteDeclContext(SS, DC)) - return ExprError(); - } else { - DependentID = true; - } - } - - if (DependentID) - return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, - IsAddressOfOperand, TemplateArgs); - // BoundsSafety: This specially handles arguments of bounds attributes // appertains to a type of C struct field such that the name lookup // within a struct finds the member name, which is not the case for other @@ -2781,7 +2753,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, &AssumedTemplate)) return ExprError(); - if (R.wasNotFoundInCurrentInstantiation()) + if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid()) return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, IsAddressOfOperand, TemplateArgs); } else { @@ -2791,7 +2763,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, // If the result might be in a dependent base class, this is a dependent // id-expression. - if (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation) + if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid()) return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, IsAddressOfOperand, TemplateArgs); @@ -2946,26 +2918,14 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, /// this path. ExprResult Sema::BuildQualifiedDeclarationNameExpr( CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, - bool IsAddressOfOperand, const Scope *S, TypeSourceInfo **RecoveryTSI) { - if (NameInfo.getName().isDependentName()) - return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(), - NameInfo, /*TemplateArgs=*/nullptr); - - DeclContext *DC = computeDeclContext(SS, false); - if (!DC) - return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(), - NameInfo, /*TemplateArgs=*/nullptr); - - if (RequireCompleteDeclContext(SS, DC)) - return ExprError(); - + bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI) { LookupResult R(*this, NameInfo, LookupOrdinaryName); - LookupQualifiedName(R, DC); + LookupParsedName(R, /*S=*/nullptr, &SS, /*ObjectType=*/QualType()); if (R.isAmbiguous()) return ExprError(); - if (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation) + if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid()) return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, /*TemplateArgs=*/nullptr); @@ -2974,6 +2934,7 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr( // diagnostic during template instantiation is likely bogus, e.g. if a class // is invalid because it's derived from an invalid base class, then missing // members were likely supposed to be inherited. + DeclContext *DC = computeDeclContext(SS); if (const auto *CD = dyn_cast<CXXRecordDecl>(DC)) if (CD->isInvalidDecl()) return ExprError(); @@ -3017,16 +2978,14 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr( return ExprEmpty(); } - // Defend against this resolving to an implicit member access. We usually - // won't get here if this might be a legitimate a class member (we end up in - // BuildMemberReferenceExpr instead), but this can be valid if we're forming - // a pointer-to-member or in an unevaluated context in C++11. - if (!R.empty() && (*R.begin())->isCXXClassMember() && !IsAddressOfOperand) + // If necessary, build an implicit class member access. + if (isPotentialImplicitMemberAccess(SS, R, IsAddressOfOperand)) return BuildPossibleImplicitMemberExpr(SS, /*TemplateKWLoc=*/SourceLocation(), - R, /*TemplateArgs=*/nullptr, S); + R, /*TemplateArgs=*/nullptr, + /*S=*/nullptr); - return BuildDeclarationNameExpr(SS, R, /* ADL */ false); + return BuildDeclarationNameExpr(SS, R, /*ADL=*/false); } /// Cast a base object to a member's actual type. @@ -3190,7 +3149,7 @@ bool Sema::UseArgumentDependentLookup(const CXXScopeSpec &SS, return false; // Never if a scope specifier was provided. - if (SS.isSet()) + if (SS.isNotEmpty()) return false; // Only in C++ or ObjC++. diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0834db95d42ad..e4d4cd7395ebf 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -2771,9 +2771,6 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, ObjectType->castAs<TagType>()->isBeingDefined()) && "Caller should have completed object type"); } else if (SS && SS->isNotEmpty()) { - if (NestedNameSpecifier *NNS = SS->getScopeRep(); - NNS->getKind() == NestedNameSpecifier::Super) - return LookupInSuper(R, NNS->getAsRecordDecl()); // This nested-name-specifier occurs after another nested-name-specifier, // so long into the context associated with the prior nested-name-specifier. if ((DC = computeDeclContext(*SS, EnteringContext))) { @@ -2781,6 +2778,12 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC)) return false; R.setContextRange(SS->getRange()); + // FIXME: '__super' lookup semantics could be implemented by a + // LookupResult::isSuperLookup flag which skips the initial search of + // the lookup context in LookupQualified. + if (NestedNameSpecifier *NNS = SS->getScopeRep(); + NNS->getKind() == NestedNameSpecifier::Super) + return LookupInSuper(R, NNS->getAsRecordDecl()); } IsDependent = !DC && isDependentScopeSpecifier(*SS); } else { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 8a7af678b33d3..de884260790cc 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -726,44 +726,22 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, bool isAddressOfOperand, const TemplateArgumentListInfo *TemplateArgs) { - DeclContext *DC = getFunctionLevelDeclContext(); - - // C++11 [expr.prim.general]p12: - // An id-expression that denotes a non-static data member or non-static - // member function of a class can only be used: - // (...) - // - if that id-expression denotes a non-static data member and it - // appears in an unevaluated operand. - // - // If this might be the case, form a DependentScopeDeclRefExpr instead of a - // CXXDependentScopeMemberExpr. The former can instantiate to either - // DeclRefExpr or MemberExpr depending on lookup results, while the latter is - // always a MemberExpr. - bool MightBeCxx11UnevalField = - getLangOpts().CPlusPlus11 && isUnevaluatedContext(); - - // Check if the nested name specifier is an enum type. - bool IsEnum = false; - if (NestedNameSpecifier *NNS = SS.getScopeRep()) - IsEnum = isa_and_nonnull<EnumType>(NNS->getAsType()); - - if (!MightBeCxx11UnevalField && !isAddressOfOperand && !IsEnum && - isa<CXXMethodDecl>(DC) && - cast<CXXMethodDecl>(DC)->isImplicitObjectMemberFunction()) { - QualType ThisType = - cast<CXXMethodDecl>(DC)->getThisType().getNonReferenceType(); - - // Since the 'this' expression is synthesized, we don't need to - // perform the double-lookup check. - NamedDecl *FirstQualifierInScope = nullptr; + if (SS.isEmpty()) { + // FIXME: This codepath is only used by dependent unqualified names + // (e.g. a dependent conversion-function-id, or operator= once we support + // it). It doesn't quite do the right thing, and it will silently fail if + // getCurrentThisType() returns null. + QualType ThisType = getCurrentThisType(); + if (ThisType.isNull()) + return ExprError(); return CXXDependentScopeMemberExpr::Create( - Context, /*This=*/nullptr, ThisType, + Context, /*Base=*/nullptr, ThisType, /*IsArrow=*/!Context.getLangOpts().HLSL, - /*Op=*/SourceLocation(), SS.getWithLocInContext(Context), TemplateKWLoc, - FirstQualifierInScope, NameInfo, TemplateArgs); + /*OperatorLoc=*/SourceLocation(), + /*QualifierLoc=*/NestedNameSpecifierLoc(), TemplateKWLoc, + /*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs); } - return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); } @@ -772,13 +750,15 @@ Sema::BuildDependentDeclRefExpr(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs) { - // DependentScopeDeclRefExpr::Create requires a valid QualifierLoc - NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); - if (!QualifierLoc) - return ExprError(); + // DependentScopeDeclRefExpr::Create requires a valid NestedNameSpecifierLoc + if (!SS.isValid()) + return CreateRecoveryExpr( + SS.getBeginLoc(), + TemplateArgs ? TemplateArgs->getRAngleLoc() : NameInfo.getEndLoc(), {}); return DependentScopeDeclRefExpr::Create( - Context, QualifierLoc, TemplateKWLoc, NameInfo, TemplateArgs); + Context, SS.getWithLocInContext(Context), TemplateKWLoc, NameInfo, + TemplateArgs); } @@ -5747,50 +5727,36 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, } // We actually only call this from template instantiation. -ExprResult -Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs) { - +ExprResult Sema::BuildQualifiedTemplateIdExpr( + CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs, bool IsAddressOfOperand) { assert(TemplateArgs || TemplateKWLoc.isValid()); - DeclContext *DC; - if (!(DC = computeDeclContext(SS, false)) || - DC->isDependentContext() || - RequireCompleteDeclContext(SS, DC)) - return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); LookupResult R(*this, NameInfo, LookupOrdinaryName); - if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(), - /*Entering*/ false, TemplateKWLoc)) + if (LookupTemplateName(R, /*S=*/nullptr, SS, /*ObjectType=*/QualType(), + /*EnteringContext=*/false, TemplateKWLoc)) return ExprError(); if (R.isAmbiguous()) return ExprError(); + if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid()) + return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); + if (R.empty()) { + DeclContext *DC = computeDeclContext(SS); Diag(NameInfo.getLoc(), diag::err_no_member) << NameInfo.getName() << DC << SS.getRange(); return ExprError(); } - auto DiagnoseTypeTemplateDecl = [&](TemplateDecl *Temp, - bool isTypeAliasTemplateDecl) { - Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) - << SS.getScopeRep() << NameInfo.getName().getAsString() << SS.getRange() - << isTypeAliasTemplateDecl; - Diag(Temp->getLocation(), diag::note_referenced_type_template) - << isTypeAliasTemplateDecl; - return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {}); - }; - - if (ClassTemplateDecl *Temp = R.getAsSingle<ClassTemplateDecl>()) - return DiagnoseTypeTemplateDecl(Temp, false); - - if (TypeAliasTemplateDecl *Temp = R.getAsSingle<TypeAliasTemplateDecl>()) - return DiagnoseTypeTemplateDecl(Temp, true); + // If necessary, build an implicit class member access. + if (isPotentialImplicitMemberAccess(SS, R, IsAddressOfOperand)) + return BuildPossibleImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs, + /*S=*/nullptr); - return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL*/ false, TemplateArgs); + return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL=*/false, TemplateArgs); } /// Form a template name from a name that is syntactically required to name a @@ -5982,8 +5948,7 @@ bool Sema::CheckTemplateTypeArgument( LookupParsedName(Result, CurScope, &SS, /*ObjectType=*/QualType()); if (Result.getAsSingle<TypeDecl>() || - Result.getResultKind() == - LookupResult::NotFoundInCurrentInstantiation) { + Result.wasNotFoundInCurrentInstantiation()) { assert(SS.getScopeRep() && "dependent scope expr must has a scope!"); // Suggest that the user add 'typename' before the NNS. SourceLocation Loc = AL.getSourceRange().getBegin(); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index f9fec21bf5bb6..d99bb20320604 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3478,11 +3478,11 @@ class TreeTransform { SS.Adopt(QualifierLoc); if (TemplateArgs || TemplateKWLoc.isValid()) - return getSema().BuildQualifiedTemplateIdExpr(SS, TemplateKWLoc, NameInfo, - TemplateArgs); + return getSema().BuildQualifiedTemplateIdExpr( + SS, TemplateKWLoc, NameInfo, TemplateArgs, IsAddressOfOperand); return getSema().BuildQualifiedDeclarationNameExpr( - SS, NameInfo, IsAddressOfOperand, /*S*/nullptr, RecoveryTSI); + SS, NameInfo, IsAddressOfOperand, RecoveryTSI); } /// Build a new template-id expression. diff --git a/clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp b/clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp index 9116e7146f812..01fa923dd1715 100644 --- a/clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp +++ b/clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp @@ -70,7 +70,7 @@ namespace test2 { } void test1() { - B<T>::foo(); + B<T>::foo(); // expected-error {{call to non-static member function without an object argument}} } static void test2() { @@ -91,8 +91,95 @@ namespace test2 { int test() { A<int> a; a.test0(); // no instantiation note here, decl is ill-formed - a.test1(); + a.test1(); // expected-note {{in instantiation}} a.test2(); // expected-note {{in instantiation}} a.test3(); // expected-note {{in instantiation}} } } + +namespace test3 { + struct A { + void f0(); + + template<typename T> + void f1(); + + static void f2(); + + template<typename T> + static void f3(); + + int x0; + + static constexpr int x1 = 0; + + template<typename T> + static constexpr int x2 = 0; + }; + + template<typename T> + struct B : T { + auto g0() -> decltype(T::f0()); + + auto g1() -> decltype(T::template f1<int>()); + + auto g2() -> decltype(T::f2()); + + auto g3() -> decltype(T::template f3<int>()); + + auto g4() -> decltype(T::x0); + + auto g5() -> decltype(T::x1); + + auto g6() -> decltype(T::template x2<int>); + + decltype(T::f0()) g7(); // expected-error {{call to non-static member function without an object argument}} + + decltype(T::template f1<int>()) g8(); // expected-error {{call to non-static member function without an object argument}} + + decltype(T::f2()) g9(); + + decltype(T::template f3<int>()) g10(); + + decltype(T::x0) g11(); + + decltype(T::x1) g12(); + + decltype(T::template x2<int>) g13(); + }; + + template struct B<A>; // expected-note {{in instantiation of}} + + template<typename T> + struct C : T { + static auto g0() -> decltype(T::f0()); // expected-error {{'this' cannot be implicitly used in a static member function declaration}} + + static auto g1() -> decltype(T::template f1<int>()); // expected-error {{'this' cannot be implicitly used in a static member function declaration}} + + static auto g2() -> decltype(T::f2()); + + static auto g3() -> decltype(T::template f3<int>()); + + static auto g4() -> decltype(T::x0); // expected-error {{'this' cannot be implicitly used in a static member function declaration}} + + static auto g5() -> decltype(T::x1); + + static auto g6() -> decltype(T::template x2<int>); + + static decltype(T::f0()) g7(); // expected-error {{call to non-static member function without an object argument}} + + static decltype(T::template f1<int>()) g8(); // expected-error {{call to non-static member function without an object argument}} + + static decltype(T::f2()) g9(); + + static decltype(T::template f3<int>()) g10(); + + static decltype(T::x0) g11(); + + static decltype(T::x1) g12(); + + static decltype(T::template x2<int>) g13(); + }; + + template struct C<A>; // expected-note {{in instantiation of}} +} diff --git a/clang/test/SemaTemplate/ms-function-specialization-class-scope.cpp b/clang/test/SemaTemplate/ms-function-specialization-class-scope.cpp index c49d2cb2422fa..e1f3ab37ad947 100644 --- a/clang/test/SemaTemplate/ms-function-specialization-class-scope.cpp +++ b/clang/test/SemaTemplate/ms-function-specialization-class-scope.cpp @@ -464,6 +464,32 @@ namespace UsesThis { g1(x1); g1(y0); g1(y1); + + T::f0(0); + T::f0(z); + T::f0(x0); + T::f0(x1); + T::f0(y0); + T::f0(y1); + T::g0(0); + T::g0(z); + T::g0(x0); + T::g0(x1); + T::g0(y0); + T::g0(y1); + + E::f1(0); + E::f1(z); + E::f1(x0); + E::f1(x1); + E::f1(y0); + E::f1(y1); + E::g1(0); + E::g1(z); + E::g1(x0); + E::g1(x1); + E::g1(y0); + E::g1(y1); } template<> @@ -519,6 +545,32 @@ namespace UsesThis { g1(x1); // expected-error {{invalid use of member 'x1' in static member function}} g1(y0); g1(y1); + + T::f0(0); // expected-error {{call to non-static member function without an object argument}} + T::f0(z); // expected-error {{call to non-static member function without an object argument}} + T::f0(x0); // expected-error {{call to non-static member function without an object argument}} + T::f0(x1); // expected-error {{call to non-static member function without an object argument}} + T::f0(y0); // expected-error {{call to non-static member function without an object argument}} + T::f0(y1); // expected-error {{call to non-static member function without an object argument}} + T::g0(0); + T::g0(z); + T::g0(x0); // expected-error {{invalid use of member 'x0' in static member function}} + T::g0(x1); // expected-error {{invalid use of member 'x1' in static member function}} + T::g0(y0); + T::g0(y1); + + E::f1(0); // expected-error {{call to non-static member function without an object argument}} + E::f1(z); // expected-error {{call to non-static member function without an object argument}} + E::f1(x0); // expected-error {{call to non-static member function without an object argument}} + E::f1(x1); // expected-error {{call to non-static member function without an object argument}} + E::f1(y0); // expected-error {{call to non-static member function without an object argument}} + E::f1(y1); // expected-error {{call to non-static member function without an object argument}} + E::g1(0); + E::g1(z); + E::g1(x0); // expected-error {{invalid use of member 'x0' in static member function}} + E::g1(x1); // expected-error {{invalid use of member 'x1' in static member function}} + E::g1(y0); + E::g1(y1); } }; diff --git a/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp index 534a5dc9ddc10..547e5945ac6bc 100644 --- a/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ b/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp @@ -102,7 +102,7 @@ class B : public A<T> { }; template class B<int>; // expected-note {{requested here}} -} +} @@ -111,8 +111,8 @@ namespace lookup_dependent_base_class_default_argument { template<class T> class A { public: - static int f1(); // expected-note {{must qualify identifier to find this declaration in dependent base class}} - int f2(); // expected-note {{must qualify identifier to find this declaration in dependent base class}} + static int f1(); // expected-note {{must qualify identifier to find this declaration in dependent base class}} + int f2(); // expected-note {{must qualify identifier to find this declaration in dependent base class}} }; template<class T> @@ -137,7 +137,7 @@ namespace lookup_dependent_base_class_friend { template <class T> class B { public: - static void g(); // expected-note {{must qualify identifier to find this declaration in dependent base class}} + static void g(); // expected-note {{must qualify identifier to find this declaration in dependent base class}} }; template <class T> @@ -228,7 +228,7 @@ template <typename T> struct C : T { int *bar() { return &b; } // expected-error {{no member named 'b' in 'PR16014::C<A>'}} expected-warning {{lookup into dependent bases}} int baz() { return T::b; } // expected-error {{no member named 'b' in 'PR16014::A'}} int T::*qux() { return &T::b; } // expected-error {{no member named 'b' in 'PR16014::A'}} - int T::*fuz() { return &U::a; } // expected-error {{use of undeclared identifier 'U'}} \ + int T::*fuz() { return &U::a; } // expected-error {{no member named 'U' in 'PR16014::C<A>'}} \ // expected-warning {{unqualified lookup into dependent bases of class template 'C'}} }; @@ -258,7 +258,7 @@ struct A : T { ::UndefClass::undef(); // expected-error {{no member named 'UndefClass' in the global namespace}} } void baz() { - B::qux(); // expected-error {{use of undeclared identifier 'B'}} \ + B::qux(); // expected-error {{no member named 'B' in 'PR19233::A<D>'}} \ // expected-warning {{unqualified lookup into dependent bases of class template 'A'}} } }; diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp index 65df513d27137..2e42b85808953 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -614,8 +614,10 @@ TEST_P(ASTMatchersTest, MemberExpr_MatchesVariable) { EXPECT_TRUE(matches("template <class T>" "class X : T { void f() { this->T::v; } };", cxxDependentScopeMemberExpr())); - EXPECT_TRUE(matches("template <class T> class X : T { void f() { T::v; } };", - cxxDependentScopeMemberExpr())); + // FIXME: Add a matcher for DependentScopeDeclRefExpr. + EXPECT_TRUE( + notMatches("template <class T> class X : T { void f() { T::v; } };", + cxxDependentScopeMemberExpr())); EXPECT_TRUE(matches("template <class T> void x() { T t; t.v; }", cxxDependentScopeMemberExpr())); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits