https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/90152
>From 6ea7f9d476910681ad01e2fe4525fb4d2c556c6f Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Thu, 25 Apr 2024 14:50:53 -0400 Subject: [PATCH 1/2] Reapply "[Clang][Sema] Diagnose class member access expressions naming non-existent members of the current instantiation prior to instantiation in the absence of dependent base classes (#84050)" Consider the following: ```cpp template<typename T> struct A { auto f() { return this->x; } }; ``` Although `A` has no dependent base classes and the lookup context for `x` is the current instantiation, we currently do not diagnose the absence of a member `x` until `A<T>::f` is instantiated. This patch moves the point of diagnosis for such expressions to occur at the point of definition (i.e. prior to instantiation). --- .../clangd/unittests/FindTargetTests.cpp | 8 +- .../unittests/SemanticHighlightingTests.cpp | 2 +- .../cppcoreguidelines/owning-memory.cpp | 2 + .../modernize/use-equals-default-copy.cpp | 12 + clang/docs/ReleaseNotes.rst | 12 + clang/include/clang/Sema/Lookup.h | 4 +- clang/include/clang/Sema/Sema.h | 14 +- clang/lib/AST/Expr.cpp | 2 +- clang/lib/Parse/ParseDecl.cpp | 2 +- clang/lib/Sema/HLSLExternalSemaSource.cpp | 7 +- clang/lib/Sema/SemaAttr.cpp | 2 +- clang/lib/Sema/SemaDecl.cpp | 7 +- clang/lib/Sema/SemaDeclCXX.cpp | 6 +- clang/lib/Sema/SemaExpr.cpp | 20 +- clang/lib/Sema/SemaExprCXX.cpp | 2 +- clang/lib/Sema/SemaExprMember.cpp | 183 +++---- clang/lib/Sema/SemaLookup.cpp | 114 ++++- clang/lib/Sema/SemaOpenMP.cpp | 17 +- clang/lib/Sema/SemaTemplate.cpp | 32 +- clang/lib/Sema/TreeTransform.h | 20 + .../AST/HLSL/this-reference-template.hlsl | 2 +- clang/test/CXX/drs/dr2xx.cpp | 10 +- clang/test/CXX/drs/dr3xx.cpp | 16 +- .../temp.res/temp.dep/temp.dep.type/p4.cpp | 456 ++++++++++++++++++ .../test/CXX/temp/temp.res/temp.local/p3.cpp | 3 +- clang/test/CodeGenCXX/mangle.cpp | 8 - .../Index/annotate-nested-name-specifier.cpp | 4 +- clang/test/SemaCXX/member-expr.cpp | 4 +- .../SemaTemplate/instantiate-function-1.cpp | 14 +- .../ASTMatchers/ASTMatchersNarrowingTest.cpp | 5 +- 30 files changed, 765 insertions(+), 225 deletions(-) create mode 100644 clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 799a549ff0816e..94437857cecca6 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -854,7 +854,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + EXPECT_DECLS("MemberExpr", "void foo()"); // Similar to above but base expression involves a function call. Code = R"cpp( @@ -872,7 +872,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + EXPECT_DECLS("MemberExpr", "void foo()"); // Similar to above but uses a function pointer. Code = R"cpp( @@ -891,7 +891,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + EXPECT_DECLS("MemberExpr", "void foo()"); // Base expression involves a member access into this. Code = R"cpp( @@ -962,7 +962,7 @@ TEST_F(TargetDeclTest, DependentExprs) { void Foo() { this->[[find]](); } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void find()"); + EXPECT_DECLS("MemberExpr", "void find()"); } TEST_F(TargetDeclTest, DependentTypes) { diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp index 4156921d83edf8..30b9b1902aa9c7 100644 --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -621,7 +621,7 @@ sizeof...($TemplateParameter[[Elements]]); struct $Class_def[[Foo]] { int $Field_decl[[Waldo]]; void $Method_def[[bar]]() { - $Class[[Foo]]().$Field_dependentName[[Waldo]]; + $Class[[Foo]]().$Field[[Waldo]]; } template $Bracket[[<]]typename $TemplateParameter_def[[U]]$Bracket[[>]] void $Method_def[[bar1]]() { diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp index 574efe7bd91478..ae61b17ca14d20 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp @@ -309,6 +309,8 @@ struct HeapArray { // Ok, since destruc HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok other._data = nullptr; // Ok + // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'std::nullptr_t' + // FIXME: This warning is emitted because an ImplicitCastExpr for the NullToPointer conversion isn't created for dependent types. other.size = 0; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp index 559031cf4d9bda..4abb9c8555970e 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp @@ -260,6 +260,8 @@ template <class T> struct Template { Template() = default; Template(const Template &Other) : Field(Other.Field) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: Template(const Template &Other) = default; Template &operator=(const Template &Other); void foo(const T &t); int Field; @@ -269,8 +271,12 @@ Template<T> &Template<T>::operator=(const Template<T> &Other) { Field = Other.Field; return *this; } +// CHECK-MESSAGES: :[[@LINE-4]]:27: warning: use '= default' +// CHECK-FIXES: Template<T> &Template<T>::operator=(const Template<T> &Other) = default; + Template<int> T1; + // Dependent types. template <class T> struct DT1 { @@ -284,6 +290,9 @@ DT1<T> &DT1<T>::operator=(const DT1<T> &Other) { Field = Other.Field; return *this; } +// CHECK-MESSAGES: :[[@LINE-4]]:17: warning: use '= default' +// CHECK-FIXES: DT1<T> &DT1<T>::operator=(const DT1<T> &Other) = default; + DT1<int> Dt1; template <class T> @@ -303,6 +312,9 @@ DT2<T> &DT2<T>::operator=(const DT2<T> &Other) { struct T { typedef int TT; }; +// CHECK-MESSAGES: :[[@LINE-8]]:17: warning: use '= default' +// CHECK-FIXES: DT2<T> &DT2<T>::operator=(const DT2<T> &Other) = default; + DT2<T> Dt2; // Default arguments. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index f5e5d3a2e6ea36..00c684e773a2e0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -385,6 +385,18 @@ Improvements to Clang's diagnostics - Clang now diagnoses requires expressions with explicit object parameters. +- Clang now looks up members of the current instantiation in the template definition context + if the current instantiation has no dependent base classes. + + .. code-block:: c++ + + template<typename T> + struct A { + int f() { + return this->x; // error: no member named 'x' in 'A<T>' + } + }; + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Sema/Lookup.h b/clang/include/clang/Sema/Lookup.h index 0db5b847038ffd..b0a08a05ac6a0a 100644 --- a/clang/include/clang/Sema/Lookup.h +++ b/clang/include/clang/Sema/Lookup.h @@ -499,7 +499,9 @@ class LookupResult { /// Note that while no result was found in the current instantiation, /// there were dependent base classes that could not be searched. void setNotFoundInCurrentInstantiation() { - assert(ResultKind == NotFound && Decls.empty()); + assert((ResultKind == NotFound || + ResultKind == NotFoundInCurrentInstantiation) && + Decls.empty()); ResultKind = NotFoundInCurrentInstantiation; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1ca523ec88c2f9..aa182b15e66ecc 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7472,7 +7472,7 @@ class Sema final : public SemaBase { bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, CXXScopeSpec &SS); bool LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, - bool AllowBuiltinCreation = false, + QualType ObjectType, bool AllowBuiltinCreation = false, bool EnteringContext = false); ObjCProtocolDecl *LookupProtocol( IdentifierInfo *II, SourceLocation IdLoc, @@ -8881,11 +8881,13 @@ class Sema final : public SemaBase { /// functions (but no function templates). FoundFunctions, }; - bool LookupTemplateName( - LookupResult &R, Scope *S, CXXScopeSpec &SS, QualType ObjectType, - bool EnteringContext, bool &MemberOfUnknownSpecialization, - RequiredTemplateKind RequiredTemplate = SourceLocation(), - AssumedTemplateKind *ATK = nullptr, bool AllowTypoCorrection = true); + + bool + LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS, + QualType ObjectType, bool EnteringContext, + RequiredTemplateKind RequiredTemplate = SourceLocation(), + AssumedTemplateKind *ATK = nullptr, + bool AllowTypoCorrection = true); TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 63dcdb919c7117..d2e40be59d6f3b 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -103,7 +103,7 @@ const Expr *Expr::skipRValueSubobjectAdjustments( } } else if (const auto *ME = dyn_cast<MemberExpr>(E)) { if (!ME->isArrow()) { - assert(ME->getBase()->getType()->isRecordType()); + assert(ME->getBase()->getType()->getAsRecordDecl()); if (const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl())) { if (!Field->isBitField() && !Field->getType()->isReferenceType()) { E = ME->getBase(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 05ad5ecbfaa0cf..53a33fa4add54e 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2998,7 +2998,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, << TokenName << TagName << getLangOpts().CPlusPlus << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName); - if (Actions.LookupParsedName(R, getCurScope(), SS)) { + if (Actions.LookupName(R, getCurScope())) { for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 1a1febf7a35241..bb283c54b3d29c 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -126,12 +126,15 @@ struct BuiltinTypeDeclBuilder { static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S, StringRef Name) { - CXXScopeSpec SS; IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); DeclarationNameInfo NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); - S.LookupParsedName(R, S.getCurScope(), &SS, false); + // AllowBuiltinCreation is false but LookupDirect will create + // the builtin when searching the global scope anyways... + S.LookupName(R, S.getCurScope()); + // FIXME: If the builtin function was user-declared in global scope, + // this assert *will* fail. Should this call LookupBuiltin instead? assert(R.isSingleResult() && "Since this is a builtin it should always resolve!"); auto *VD = cast<ValueDecl>(R.getFoundDecl()); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index a5dd158808f26b..a83b1e8afadbc6 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -837,7 +837,7 @@ void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope, IdentifierInfo *Name = IdTok.getIdentifierInfo(); LookupResult Lookup(*this, Name, IdTok.getLocation(), LookupOrdinaryName); - LookupParsedName(Lookup, curScope, nullptr, true); + LookupName(Lookup, curScope, /*AllowBuiltinCreation=*/true); if (Lookup.empty()) { Diag(PragmaLoc, diag::warn_pragma_unused_undeclared_var) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e0745fe9a45367..4e275dc15fbb4e 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -832,7 +832,7 @@ static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, IdentifierInfo *&Name, SourceLocation NameLoc) { LookupResult R(SemaRef, Name, NameLoc, Sema::LookupTagName); - SemaRef.LookupParsedName(R, S, &SS); + SemaRef.LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); if (TagDecl *Tag = R.getAsSingle<TagDecl>()) { StringRef FixItTagName; switch (Tag->getTagKind()) { @@ -869,7 +869,7 @@ static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, // Replace lookup results with just the tag decl. Result.clear(Sema::LookupTagName); - SemaRef.LookupParsedName(Result, S, &SS); + SemaRef.LookupParsedName(Result, S, &SS, /*ObjectType=*/QualType()); return true; } @@ -896,7 +896,8 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, } LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); - LookupParsedName(Result, S, &SS, !CurMethod); + LookupParsedName(Result, S, &SS, /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/!CurMethod); if (SS.isInvalid()) return NameClassification::Error(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index abdbc9d8830c03..4d5836720a651f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -4517,7 +4517,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD, DS.getBeginLoc(), DS.getEllipsisLoc()); } else { LookupResult R(*this, MemberOrBase, IdLoc, LookupOrdinaryName); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); TypeDecl *TyD = R.getAsSingle<TypeDecl>(); if (!TyD) { @@ -12262,7 +12262,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, // Lookup namespace name. LookupResult R(*this, NamespcName, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); if (R.isAmbiguous()) return nullptr; @@ -13721,7 +13721,7 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, // Lookup the namespace name. LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); if (R.isAmbiguous()) return nullptr; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 50f92c496a539a..0c37f43f75401b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -673,8 +673,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { // expressions of certain types in C++. if (getLangOpts().CPlusPlus && (E->getType() == Context.OverloadTy || - T->isDependentType() || - T->isRecordType())) + // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied + // to pointer types even if the pointee type is dependent. + (T->isDependentType() && !T->isPointerType()) || T->isRecordType())) return E; // The C standard is actually really unclear on this point, and @@ -2751,8 +2752,8 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, if (isBoundsAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) { // See if this is reference to a field of struct. LookupResult R(*this, NameInfo, LookupMemberName); - // LookupParsedName handles a name lookup from within anonymous struct. - if (LookupParsedName(R, S, &SS)) { + // LookupName handles a name lookup from within anonymous struct. + if (LookupName(R, S)) { if (auto *VD = dyn_cast<ValueDecl>(R.getFoundDecl())) { QualType type = VD->getType().getNonReferenceType(); // This will eventually be translated into MemberExpr upon @@ -2773,20 +2774,19 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, // lookup to determine that it was a template name in the first place. If // this becomes a performance hit, we can work harder to preserve those // results until we get here but it's likely not worth it. - bool MemberOfUnknownSpecialization; AssumedTemplateKind AssumedTemplate; - if (LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, - MemberOfUnknownSpecialization, TemplateKWLoc, + if (LookupTemplateName(R, S, SS, /*ObjectType=*/QualType(), + /*EnteringContext=*/false, TemplateKWLoc, &AssumedTemplate)) return ExprError(); - if (MemberOfUnknownSpecialization || - (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)) + if (R.wasNotFoundInCurrentInstantiation()) return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, IsAddressOfOperand, TemplateArgs); } else { bool IvarLookupFollowUp = II && !SS.isSet() && getCurMethodDecl(); - LookupParsedName(R, S, &SS, !IvarLookupFollowUp); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/!IvarLookupFollowUp); // If the result might be in a dependent base class, this is a dependent // id-expression. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 779a41620033dc..c1cb03e4ec7ae2 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -9157,7 +9157,7 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, // Do the redeclaration lookup in the current scope. LookupResult R(*this, TargetNameInfo, Sema::LookupAnyName, RedeclarationKind::NotForRedeclaration); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); R.suppressDiagnostics(); switch (R.getResultKind()) { diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 6e30716b9ae436..14dde1bff8fbc6 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -667,8 +667,8 @@ namespace { // classes, one of its base classes. class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback { public: - explicit RecordMemberExprValidatorCCC(const RecordType *RTy) - : Record(RTy->getDecl()) { + explicit RecordMemberExprValidatorCCC(QualType RTy) + : Record(RTy->getAsRecordDecl()) { // Don't add bare keywords to the consumer since they will always fail // validation by virtue of not being associated with any decls. WantTypeSpecifiers = false; @@ -713,58 +713,36 @@ class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback { } static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, - Expr *BaseExpr, - const RecordType *RTy, + Expr *BaseExpr, QualType RTy, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, bool HasTemplateArgs, SourceLocation TemplateKWLoc, TypoExpr *&TE) { SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange(); - RecordDecl *RDecl = RTy->getDecl(); - if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) && - SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), - diag::err_typecheck_incomplete_tag, - BaseRange)) + if (!RTy->isDependentType() && + !SemaRef.isThisOutsideMemberFunctionBody(RTy) && + SemaRef.RequireCompleteType( + OpLoc, RTy, diag::err_typecheck_incomplete_tag, BaseRange)) return true; - if (HasTemplateArgs || TemplateKWLoc.isValid()) { - // LookupTemplateName doesn't expect these both to exist simultaneously. - QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); + // LookupTemplateName/LookupParsedName don't expect these both to exist + // simultaneously. + QualType ObjectType = SS.isSet() ? QualType() : RTy; + if (HasTemplateArgs || TemplateKWLoc.isValid()) + return SemaRef.LookupTemplateName(R, + /*S=*/nullptr, SS, ObjectType, + /*EnteringContext=*/false, TemplateKWLoc); - bool MOUS; - return SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS, - TemplateKWLoc); - } - - DeclContext *DC = RDecl; - if (SS.isSet()) { - // If the member name was a qualified-id, look into the - // nested-name-specifier. - DC = SemaRef.computeDeclContext(SS, false); - - if (SemaRef.RequireCompleteDeclContext(SS, DC)) { - SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag) - << SS.getRange() << DC; - return true; - } - - assert(DC && "Cannot handle non-computable dependent contexts in lookup"); - - if (!isa<TypeDecl>(DC)) { - SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_nonclass) - << DC << SS.getRange(); - return true; - } - } + SemaRef.LookupParsedName(R, /*S=*/nullptr, &SS, ObjectType); - // The record definition is complete, now look up the member. - SemaRef.LookupQualifiedName(R, DC, SS); - - if (!R.empty()) + if (!R.empty() || R.wasNotFoundInCurrentInstantiation()) return false; DeclarationName Typo = R.getLookupName(); SourceLocation TypoLoc = R.getNameLoc(); + // Recompute the lookup context. + DeclContext *DC = SS.isSet() ? SemaRef.computeDeclContext(SS) + : SemaRef.computeDeclContext(RTy); struct QueryState { Sema &SemaRef; @@ -788,7 +766,8 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, << Typo << DC << DroppedSpecifier << SS.getRange()); } else { - SemaRef.Diag(TypoLoc, diag::err_no_member) << Typo << DC << BaseRange; + SemaRef.Diag(TypoLoc, diag::err_no_member) + << Typo << DC << (SS.isSet() ? SS.getRange() : BaseRange); } }, [=](Sema &SemaRef, TypoExpr *TE, TypoCorrection TC) mutable { @@ -814,34 +793,25 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, Decl *ObjCImpDecl, bool HasTemplateArgs, SourceLocation TemplateKWLoc); -ExprResult -Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, - SourceLocation OpLoc, bool IsArrow, - CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, - const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs, - const Scope *S, - ActOnMemberAccessExtraArgs *ExtraArgs) { - if (BaseType->isDependentType() || - (SS.isSet() && isDependentScopeSpecifier(SS)) || - NameInfo.getName().isDependentName()) - return ActOnDependentMemberExpr(Base, BaseType, - IsArrow, OpLoc, - SS, TemplateKWLoc, FirstQualifierInScope, - NameInfo, TemplateArgs); - +ExprResult Sema::BuildMemberReferenceExpr( + Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, + CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs, const Scope *S, + ActOnMemberAccessExtraArgs *ExtraArgs) { LookupResult R(*this, NameInfo, LookupMemberName); + if (SS.isInvalid()) + return ExprError(); + // Implicit member accesses. if (!Base) { TypoExpr *TE = nullptr; QualType RecordTy = BaseType; if (IsArrow) RecordTy = RecordTy->castAs<PointerType>()->getPointeeType(); - if (LookupMemberExprInRecord( - *this, R, nullptr, RecordTy->castAs<RecordType>(), OpLoc, IsArrow, - SS, TemplateArgs != nullptr, TemplateKWLoc, TE)) + if (LookupMemberExprInRecord(*this, R, nullptr, RecordTy, OpLoc, IsArrow, + SS, TemplateArgs != nullptr, TemplateKWLoc, + TE)) return ExprError(); if (TE) return TE; @@ -1033,6 +1003,12 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, const Scope *S, bool SuppressQualifierCheck, ActOnMemberAccessExtraArgs *ExtraArgs) { + assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid"); + if (R.wasNotFoundInCurrentInstantiation()) + return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS, + TemplateKWLoc, FirstQualifierInScope, + R.getLookupNameInfo(), TemplateArgs); + QualType BaseType = BaseExprType; if (IsArrow) { assert(BaseType->isPointerType()); @@ -1040,6 +1016,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, } R.setBaseObjectType(BaseType); + assert((SS.isEmpty() + ? !BaseType->isDependentType() || computeDeclContext(BaseType) + : !isDependentScopeSpecifier(SS) || computeDeclContext(SS)) && + "dependent lookup context that isn't the current instantiation?"); + // C++1z [expr.ref]p2: // For the first option (dot) the first expression shall be a glvalue [...] if (!IsArrow && BaseExpr && BaseExpr->isPRValue()) { @@ -1069,13 +1050,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, if (R.empty()) { // Rederive where we looked up. - DeclContext *DC = (SS.isSet() - ? computeDeclContext(SS, false) - : BaseType->castAs<RecordType>()->getDecl()); - + DeclContext *DC = + (SS.isSet() ? computeDeclContext(SS) : computeDeclContext(BaseType)); if (ExtraArgs) { ExprResult RetryExpr; - if (!IsArrow && BaseExpr) { + if (!IsArrow && BaseExpr && !BaseExpr->isTypeDependent()) { SFINAETrap Trap(*this, true); ParsedType ObjectType; bool MayBePseudoDestructor = false; @@ -1098,9 +1077,12 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, } } + assert(DC); Diag(R.getNameLoc(), diag::err_no_member) - << MemberName << DC - << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); + << MemberName << DC + << (SS.isSet() + ? SS.getRange() + : (BaseExpr ? BaseExpr->getSourceRange() : SourceRange())); return ExprError(); } @@ -1330,7 +1312,6 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, return ExprError(); QualType BaseType = BaseExpr.get()->getType(); - assert(!BaseType->isDependentType()); DeclarationName MemberName = R.getLookupName(); SourceLocation MemberLoc = R.getNameLoc(); @@ -1342,29 +1323,31 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, if (IsArrow) { if (const PointerType *Ptr = BaseType->getAs<PointerType>()) BaseType = Ptr->getPointeeType(); - else if (const ObjCObjectPointerType *Ptr - = BaseType->getAs<ObjCObjectPointerType>()) - BaseType = Ptr->getPointeeType(); - else if (BaseType->isRecordType()) { - // Recover from arrow accesses to records, e.g.: - // struct MyRecord foo; - // foo->bar - // This is actually well-formed in C++ if MyRecord has an - // overloaded operator->, but that should have been dealt with - // by now--or a diagnostic message already issued if a problem - // was encountered while looking for the overloaded operator->. - if (!S.getLangOpts().CPlusPlus) { - S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() - << FixItHint::CreateReplacement(OpLoc, "."); + else if (!BaseType->isDependentType()) { + if (const ObjCObjectPointerType *Ptr = + BaseType->getAs<ObjCObjectPointerType>()) + BaseType = Ptr->getPointeeType(); + else if (BaseType->isRecordType()) { + // Recover from arrow accesses to records, e.g.: + // struct MyRecord foo; + // foo->bar + // This is actually well-formed in C++ if MyRecord has an + // overloaded operator->, but that should have been dealt with + // by now--or a diagnostic message already issued if a problem + // was encountered while looking for the overloaded operator->. + if (!S.getLangOpts().CPlusPlus) { + S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, "."); + } + IsArrow = false; + } else if (BaseType->isFunctionType()) { + goto fail; + } else { + S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) + << BaseType << BaseExpr.get()->getSourceRange(); + return ExprError(); } - IsArrow = false; - } else if (BaseType->isFunctionType()) { - goto fail; - } else { - S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) - << BaseType << BaseExpr.get()->getSourceRange(); - return ExprError(); } } @@ -1384,10 +1367,10 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, } // Handle field access to simple records. - if (const RecordType *RTy = BaseType->getAs<RecordType>()) { + if (BaseType->getAsRecordDecl() || BaseType->isDependentType()) { TypoExpr *TE = nullptr; - if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, OpLoc, IsArrow, SS, - HasTemplateArgs, TemplateKWLoc, TE)) + if (LookupMemberExprInRecord(S, R, BaseExpr.get(), BaseType, OpLoc, IsArrow, + SS, HasTemplateArgs, TemplateKWLoc, TE)) return ExprError(); // Returning valid-but-null is how we indicate to the caller that @@ -1810,7 +1793,6 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, DecomposeUnqualifiedId(Id, TemplateArgsBuffer, NameInfo, TemplateArgs); - DeclarationName Name = NameInfo.getName(); bool IsArrow = (OpKind == tok::arrow); if (getLangOpts().HLSL && IsArrow) @@ -1824,13 +1806,6 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, if (Result.isInvalid()) return ExprError(); Base = Result.get(); - if (Base->getType()->isDependentType() || Name.isDependentName() || - isDependentScopeSpecifier(SS)) { - return ActOnDependentMemberExpr(Base, Base->getType(), IsArrow, OpLoc, SS, - TemplateKWLoc, FirstQualifierInScope, - NameInfo, TemplateArgs); - } - ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl}; ExprResult Res = BuildMemberReferenceExpr( Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 55af414df39f51..2f6ad49fc08b61 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1282,6 +1282,31 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { if (DeclContext *DC = PreS->getEntity()) DeclareImplicitMemberFunctionsWithName(*this, Name, R.getNameLoc(), DC); } + // C++23 [temp.dep.general]p2: + // The component name of an unqualified-id is dependent if + // - it is a conversion-function-id whose conversion-type-id + // is dependent, or + // - it is operator= and the current class is a templated entity, or + // - the unqualified-id is the postfix-expression in a dependent call. + if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && + Name.getCXXNameType()->isDependentType()) { + R.setNotFoundInCurrentInstantiation(); + return false; + } + + // If this is the name of an implicitly-declared special member function, + // go through the scope stack to implicitly declare + if (isImplicitlyDeclaredMemberFunctionName(Name)) { + for (Scope *PreS = S; PreS; PreS = PreS->getParent()) + if (DeclContext *DC = PreS->getEntity()) { + if (DC->isDependentContext() && isa<CXXRecordDecl>(DC) && + Name.getCXXOverloadedOperator() == OO_Equal) { + R.setNotFoundInCurrentInstantiation(); + return false; + } + DeclareImplicitMemberFunctionsWithName(*this, Name, R.getNameLoc(), DC); + } + } // Implicitly declare member functions with the name we're looking for, if in // fact we are in a scope where it matters. @@ -2446,10 +2471,33 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, } } QL(LookupCtx); + CXXRecordDecl *LookupRec = dyn_cast<CXXRecordDecl>(LookupCtx); + // FIXME: Per [temp.dep.general]p2, an unqualified name is also dependent + // if it's a dependent conversion-function-id or operator= where the current + // class is a templated entity. This should be handled in LookupName. + if (!InUnqualifiedLookup && !R.isForRedeclaration()) { + // C++23 [temp.dep.type]p5: + // A qualified name is dependent if + // - it is a conversion-function-id whose conversion-type-id + // is dependent, or + // - [...] + // - its lookup context is the current instantiation and it + // is operator=, or + // - [...] + if (DeclarationName Name = R.getLookupName(); + (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && + Name.getCXXNameType()->isDependentType()) || + (Name.getCXXOverloadedOperator() == OO_Equal && LookupRec && + LookupRec->isDependentContext())) { + R.setNotFoundInCurrentInstantiation(); + return false; + } + } + if (LookupDirect(*this, R, LookupCtx)) { R.resolveKind(); - if (isa<CXXRecordDecl>(LookupCtx)) - R.setNamingClass(cast<CXXRecordDecl>(LookupCtx)); + if (LookupRec) + R.setNamingClass(LookupRec); return true; } @@ -2471,7 +2519,6 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, // If this isn't a C++ class, we aren't allowed to look into base // classes, we're done. - CXXRecordDecl *LookupRec = dyn_cast<CXXRecordDecl>(LookupCtx); if (!LookupRec || !LookupRec->getDefinition()) return false; @@ -2718,38 +2765,54 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, /// /// @returns True if any decls were found (but possibly ambiguous) bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, - bool AllowBuiltinCreation, bool EnteringContext) { - if (SS && SS->isInvalid()) { - // When the scope specifier is invalid, don't even look for - // anything. + QualType ObjectType, bool AllowBuiltinCreation, + bool EnteringContext) { + // When the scope specifier is invalid, don't even look for anything. + if (SS && SS->isInvalid()) return false; - } - if (SS && SS->isSet()) { - NestedNameSpecifier *NNS = SS->getScopeRep(); - if (NNS->getKind() == NestedNameSpecifier::Super) + // Determine where to perform name lookup + DeclContext *DC = nullptr; + bool IsDependent = false; + if (!ObjectType.isNull()) { + // This nested-name-specifier occurs in a member access expression, e.g., + // x->B::f, and we are looking into the type of the object. + assert((!SS || SS->isEmpty()) && + "ObjectType and scope specifier cannot coexist"); + DC = computeDeclContext(ObjectType); + IsDependent = !DC && ObjectType->isDependentType(); + assert(((!DC && ObjectType->isDependentType()) || + !ObjectType->isIncompleteType() || !ObjectType->getAs<TagType>() || + 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()); - - if (DeclContext *DC = computeDeclContext(*SS, EnteringContext)) { - // We have resolved the scope specifier to a particular declaration - // contex, and will perform name lookup in that context. + // 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))) { + // The declaration context must be complete. if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC)) return false; - R.setContextRange(SS->getRange()); - return LookupQualifiedName(R, DC); } + IsDependent = !DC && isDependentScopeSpecifier(*SS); + } else { + // Perform unqualified name lookup starting in the given scope. + return LookupName(R, S, AllowBuiltinCreation); + } + // If we were able to compute a declaration context, perform qualified name + // lookup in that context. + if (DC) + return LookupQualifiedName(R, DC); + else if (IsDependent) // We could not resolve the scope specified to a specific declaration // context, which means that SS refers to an unknown specialization. // Name lookup can't find anything in this case. R.setNotFoundInCurrentInstantiation(); - R.setContextRange(SS->getRange()); - return false; - } - - // Perform unqualified name lookup starting in the given scope. - return LookupName(R, S, AllowBuiltinCreation); + return false; } /// Perform qualified name lookup into all base classes of the given @@ -5018,8 +5081,9 @@ static void LookupPotentialTypoResult(Sema &SemaRef, return; } - SemaRef.LookupParsedName(Res, S, SS, /*AllowBuiltinCreation=*/false, - EnteringContext); + SemaRef.LookupParsedName(Res, S, SS, + /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/false, EnteringContext); // Fake ivar lookup; this should really be part of // LookupParsedName. diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index cee8da495c5495..cf5447f223d450 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3061,7 +3061,9 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, OpenMPDirectiveKind Kind) { ASTContext &Context = getASTContext(); LookupResult Lookup(SemaRef, Id, Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, true); + SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, + /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/true); if (Lookup.isAmbiguous()) return ExprError(); @@ -7407,7 +7409,8 @@ void SemaOpenMP::ActOnStartOfFunctionDefinitionInOpenMPDeclareVariantScope( const IdentifierInfo *BaseII = D.getIdentifier(); LookupResult Lookup(SemaRef, DeclarationName(BaseII), D.getIdentifierLoc(), Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, S, &D.getCXXScopeSpec()); + SemaRef.LookupParsedName(Lookup, S, &D.getCXXScopeSpec(), + /*ObjectType=*/QualType()); TypeSourceInfo *TInfo = SemaRef.GetTypeForDeclarator(D); QualType FType = TInfo->getType(); @@ -19311,7 +19314,8 @@ buildDeclareReductionRef(Sema &SemaRef, SourceLocation Loc, SourceRange Range, if (S) { LookupResult Lookup(SemaRef, ReductionId, Sema::LookupOMPReductionName); Lookup.suppressDiagnostics(); - while (S && SemaRef.LookupParsedName(Lookup, S, &ReductionIdScopeSpec)) { + while (S && SemaRef.LookupParsedName(Lookup, S, &ReductionIdScopeSpec, + /*ObjectType=*/QualType())) { NamedDecl *D = Lookup.getRepresentativeDecl(); do { S = S->getParent(); @@ -22180,7 +22184,8 @@ static ExprResult buildUserDefinedMapperRef(Sema &SemaRef, Scope *S, LookupResult Lookup(SemaRef, MapperId, Sema::LookupOMPMapperName); Lookup.suppressDiagnostics(); if (S) { - while (S && SemaRef.LookupParsedName(Lookup, S, &MapperIdScopeSpec)) { + while (S && SemaRef.LookupParsedName(Lookup, S, &MapperIdScopeSpec, + /*ObjectType=*/QualType())) { NamedDecl *D = Lookup.getRepresentativeDecl(); while (S && !S->isDeclScope(D)) S = S->getParent(); @@ -23497,7 +23502,9 @@ void SemaOpenMP::DiagnoseUnterminatedOpenMPDeclareTarget() { NamedDecl *SemaOpenMP::lookupOpenMPDeclareTargetName( Scope *CurScope, CXXScopeSpec &ScopeSpec, const DeclarationNameInfo &Id) { LookupResult Lookup(SemaRef, Id, Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, true); + SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, + /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/true); if (Lookup.isAmbiguous()) return nullptr; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index bbcb7c33a98579..72bf6370ca821a 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -210,10 +210,11 @@ TemplateNameKind Sema::isTemplateName(Scope *S, AssumedTemplateKind AssumedTemplate; LookupResult R(*this, TName, Name.getBeginLoc(), LookupOrdinaryName); if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext, - MemberOfUnknownSpecialization, SourceLocation(), + /*RequiredTemplate=*/SourceLocation(), &AssumedTemplate, /*AllowTypoCorrection=*/!Disambiguation)) return TNK_Non_template; + MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation(); if (AssumedTemplate != AssumedTemplateKind::None) { TemplateResult = TemplateTy::make(Context.getAssumedTemplateName(TName)); @@ -320,15 +321,12 @@ TemplateNameKind Sema::isTemplateName(Scope *S, bool Sema::isDeductionGuideName(Scope *S, const IdentifierInfo &Name, SourceLocation NameLoc, CXXScopeSpec &SS, ParsedTemplateTy *Template /*=nullptr*/) { - bool MemberOfUnknownSpecialization = false; - // We could use redeclaration lookup here, but we don't need to: the // syntactic form of a deduction guide is enough to identify it even // if we can't look up the template name at all. LookupResult R(*this, DeclarationName(&Name), NameLoc, LookupOrdinaryName); if (LookupTemplateName(R, S, SS, /*ObjectType*/ QualType(), - /*EnteringContext*/ false, - MemberOfUnknownSpecialization)) + /*EnteringContext*/ false)) return false; if (R.empty()) return false; @@ -374,11 +372,8 @@ bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II, return true; } -bool Sema::LookupTemplateName(LookupResult &Found, - Scope *S, CXXScopeSpec &SS, - QualType ObjectType, - bool EnteringContext, - bool &MemberOfUnknownSpecialization, +bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, + QualType ObjectType, bool EnteringContext, RequiredTemplateKind RequiredTemplate, AssumedTemplateKind *ATK, bool AllowTypoCorrection) { @@ -391,7 +386,6 @@ bool Sema::LookupTemplateName(LookupResult &Found, Found.setTemplateNameLookup(true); // Determine where to perform name lookup - MemberOfUnknownSpecialization = false; DeclContext *LookupCtx = nullptr; bool IsDependent = false; if (!ObjectType.isNull()) { @@ -548,7 +542,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup); if (Found.empty()) { if (IsDependent) { - MemberOfUnknownSpecialization = true; + Found.setNotFoundInCurrentInstantiation(); return false; } @@ -5595,11 +5589,9 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, RequireCompleteDeclContext(SS, DC)) return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); - bool MemberOfUnknownSpecialization; LookupResult R(*this, NameInfo, LookupOrdinaryName); if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(), - /*Entering*/false, MemberOfUnknownSpecialization, - TemplateKWLoc)) + /*Entering*/ false, TemplateKWLoc)) return ExprError(); if (R.isAmbiguous()) @@ -5720,14 +5712,13 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S, DeclarationNameInfo DNI = GetNameFromUnqualifiedId(Name); LookupResult R(*this, DNI.getName(), Name.getBeginLoc(), LookupOrdinaryName); - bool MOUS; // Tell LookupTemplateName that we require a template so that it diagnoses // cases where it finds a non-template. RequiredTemplateKind RTK = TemplateKWLoc.isValid() ? RequiredTemplateKind(TemplateKWLoc) : TemplateNameIsRequired; - if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, MOUS, - RTK, nullptr, /*AllowTypoCorrection=*/false) && + if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, RTK, + /*ATK=*/nullptr, /*AllowTypoCorrection=*/false) && !R.isAmbiguous()) { if (LookupCtx) Diag(Name.getBeginLoc(), diag::err_no_member) @@ -5816,7 +5807,7 @@ bool Sema::CheckTemplateTypeArgument( if (auto *II = NameInfo.getName().getAsIdentifierInfo()) { LookupResult Result(*this, NameInfo, LookupOrdinaryName); - LookupParsedName(Result, CurScope, &SS); + LookupParsedName(Result, CurScope, &SS, /*ObjectType=*/QualType()); if (Result.getAsSingle<TypeDecl>() || Result.getResultKind() == @@ -11179,7 +11170,8 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, : TSK_ExplicitInstantiationDeclaration; LookupResult Previous(*this, NameInfo, LookupOrdinaryName); - LookupParsedName(Previous, S, &D.getCXXScopeSpec()); + LookupParsedName(Previous, S, &D.getCXXScopeSpec(), + /*ObjectType=*/QualType()); if (!R->isFunctionType()) { // C++ [temp.explicit]p1: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index f47bc219e6fa32..28d3d1b79a7424 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13217,6 +13217,26 @@ bool TreeTransform<Derived>::TransformOverloadExprDecls(OverloadExpr *Old, // Resolve a kind, but don't do any further analysis. If it's // ambiguous, the callee needs to deal with it. R.resolveKind(); + + if (Old->hasTemplateKeyword() && !R.empty()) { + NamedDecl *FoundDecl = R.getRepresentativeDecl()->getUnderlyingDecl(); + getSema().FilterAcceptableTemplateNames(R, + /*AllowFunctionTemplates=*/true, + /*AllowDependent=*/true); + if (R.empty()) { + // If a 'template' keyword was used, a lookup that finds only non-template + // names is an error. + getSema().Diag(R.getNameLoc(), + diag::err_template_kw_refers_to_non_template) + << R.getLookupName() << Old->getQualifierLoc().getSourceRange() + << Old->hasTemplateKeyword() << Old->getTemplateKeywordLoc(); + getSema().Diag(FoundDecl->getLocation(), + diag::note_template_kw_refers_to_non_template) + << R.getLookupName(); + return true; + } + } + return false; } diff --git a/clang/test/AST/HLSL/this-reference-template.hlsl b/clang/test/AST/HLSL/this-reference-template.hlsl index 60e057986ebf80..d427e73044b788 100644 --- a/clang/test/AST/HLSL/this-reference-template.hlsl +++ b/clang/test/AST/HLSL/this-reference-template.hlsl @@ -24,7 +24,7 @@ void main() { // CHECK: -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:8:3, line:10:3> line:8:5 getFirst 'K ()' implicit-inline // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:16, line:10:3> // CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:9:4, col:16> -// CHECK-NEXT:-CXXDependentScopeMemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> '<dependent type>' lvalue .First +// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'K' lvalue .First 0x{{[0-9A-Fa-f]+}} // CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:11> 'Pair<K, V>' lvalue this // CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:12:3, line:14:3> line:12:5 getSecond 'V ()' implicit-inline // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:17, line:14:3> diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp index 5d3e8ce4bea3bc..2b3131be33057a 100644 --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -561,9 +561,9 @@ namespace cwg244 { // cwg244: 11 B_ptr->B_alias::~B(); B_ptr->B_alias::~B_alias(); B_ptr->cwg244::~B(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg244'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg244'}} B_ptr->cwg244::~B_alias(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg244'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg244'}} } template<typename T, typename U> @@ -836,7 +836,7 @@ namespace cwg258 { // cwg258: 2.8 namespace cwg259 { // cwg259: 4 template<typename T> struct A {}; - template struct A<int>; // #cwg259-A-int + template struct A<int>; // #cwg259-A-int template struct A<int>; // expected-error@-1 {{duplicate explicit instantiation of 'A<int>'}} // expected-note@#cwg259-A-int {{previous explicit instantiation is here}} @@ -997,7 +997,7 @@ namespace cwg275 { // cwg275: no // expected-error@-1 {{no function template matches function template specialization 'f'}} } - template <class T> void g(T) {} // #cwg275-g + template <class T> void g(T) {} // #cwg275-g template <> void N::f(char) {} template <> void f(int) {} @@ -1164,7 +1164,7 @@ namespace cwg285 { // cwg285: yes namespace cwg286 { // cwg286: 2.8 template<class T> struct A { class C { - template<class T2> struct B {}; // #cwg286-B + template<class T2> struct B {}; // #cwg286-B }; }; diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp index 3e9228fe21fb64..94227dc031c6ab 100644 --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -34,7 +34,7 @@ namespace cwg301 { // cwg301: 3.5 bool b = (void(*)(S, S))operator- < (void(*)(S, S))operator-; // cxx98-17-warning@-1 {{ordered comparison of function pointers ('void (*)(S, S)' and 'void (*)(S, S)')}} // cxx20-23-error@-2 {{expected '>'}} - // cxx20-23-note@-3 {{to match this '<'}} + // cxx20-23-note@-3 {{to match this '<'}} bool c = (void(*)(S, S))operator+ < (void(*)(S, S))operator-; // expected-error@-1 {{expected '>'}} // expected-note@-2 {{to match this '<'}} @@ -642,7 +642,7 @@ namespace cwg339 { // cwg339: 2.8 char xxx(int); char (&xxx(float))[2]; - template<class T> A<sizeof(xxx((T)0))> f(T) {} // #cwg339-f + template<class T> A<sizeof(xxx((T)0))> f(T) {} // #cwg339-f void test() { A<1> a = f(0); @@ -828,7 +828,7 @@ namespace cwg352 { // cwg352: 2.8 void g(A::E e) { foo(e, &arg); // expected-error@-1 {{no matching function for call to 'foo'}} - // expected-note@#cwg352-foo {{candidate template ignored: couldn't infer template argument 'R'}} + // expected-note@#cwg352-foo {{candidate template ignored: couldn't infer template argument 'R'}} using A::foo; foo<int, int>(e, &arg); // ok, uses non-template @@ -929,7 +929,7 @@ namespace cwg352 { // cwg352: 2.8 namespace example5 { template<int I> class A {}; - template<int I> void g(A<I+1>); // #cwg352-g + template<int I> void g(A<I+1>); // #cwg352-g template<int I> void f(A<I>, A<I+1>); void h(A<1> a1, A<2> a2) { g(a1); @@ -1256,7 +1256,7 @@ namespace cwg373 { // cwg373: 5 } }; - struct A { struct B {}; }; // #cwg373-A + struct A { struct B {}; }; // #cwg373-A namespace X = A::B; // expected-error@-1 {{expected namespace name}} // expected-note@#cwg373-A {{'A' declared here}} @@ -1608,7 +1608,7 @@ namespace cwg395 { // cwg395: 3.0 // expected-error@-2 {{conversion function cannot have any parameters}} // expected-error@-3 {{cannot specify any part of a return type in the declaration of a conversion function}} // expected-error@-4 {{conversion function cannot convert to a function type}} - + }; struct null1_t { @@ -1721,9 +1721,9 @@ namespace cwg399 { // cwg399: 11 B_ptr->B_alias::~B(); B_ptr->B_alias::~B_alias(); B_ptr->cwg399::~B(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg399'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg399'}} B_ptr->cwg399::~B_alias(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg399'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg399'}} } template<typename T, typename U> diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp new file mode 100644 index 00000000000000..b1d2859be863aa --- /dev/null +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp @@ -0,0 +1,456 @@ +// RUN: %clang_cc1 -Wno-unused-value -verify %s + +namespace N0 { + struct A { + int x0; + static int y0; + int x1; + static int y1; + + void f0(); + static void g0(); + void f1(); + static void g1(); + + using M0 = int; + using M1 = int; + + struct C0 { }; + struct C1 { }; + }; + + template<typename T> + struct B : A { + int x2; + static int y2; + + void f2(); + static void g2(); + + using M2 = int; + + struct C2 { }; + + using A::x1; + using A::y1; + using A::f1; + using A::g1; + using A::M1; + using A::C1; + + using T::x3; + using T::y3; + using T::f3; + using T::g3; + using typename T::M3; + using typename T::C3; + + void not_instantiated(B *a, B &b) { + // All of the following should be found in the current instantiation. + + new M0; + new B::M0; + new A::M0; + new B::A::M0; + new C0; + new B::C0; + new A::C0; + new B::A::C0; + new M1; + new B::M1; + new A::M1; + new B::A::M1; + new C1; + new B::C1; + new A::C1; + new B::A::C1; + new M2; + new B::M2; + new C2; + new B::C2; + new M3; + new B::M3; + new C3; + new B::C3; + + x0; + B::x0; + A::x0; + B::A::x0; + y0; + B::y0; + A::y0; + B::A::y0; + x1; + B::x1; + A::x1; + B::A::x1; + y1; + B::y1; + A::y1; + B::A::y1; + x2; + B::x2; + y2; + B::y2; + x3; + B::x3; + y3; + B::y3; + + f0(); + B::f0(); + A::f0(); + B::A::f0(); + g0(); + B::g0(); + A::g0(); + B::A::g0(); + f1(); + B::f1(); + A::f1(); + B::A::f1(); + g1(); + B::g1(); + A::g1(); + B::A::g1(); + f2(); + B::f2(); + g2(); + B::g2(); + f3(); + B::f3(); + g3(); + B::g3(); + + this->x0; + this->B::x0; + this->A::x0; + this->B::A::x0; + this->y0; + this->B::y0; + this->A::y0; + this->B::A::y0; + this->x1; + this->B::x1; + this->A::x1; + this->B::A::x1; + this->y1; + this->B::y1; + this->A::y1; + this->B::A::y1; + this->x2; + this->B::x2; + this->y2; + this->B::y2; + this->x3; + this->B::x3; + this->y3; + this->B::y3; + + this->f0(); + this->B::f0(); + this->A::f0(); + this->B::A::f0(); + this->g0(); + this->B::g0(); + this->A::g0(); + this->B::A::g0(); + this->f1(); + this->B::f1(); + this->A::f1(); + this->B::A::f1(); + this->g1(); + this->B::g1(); + this->A::g1(); + this->B::A::g1(); + this->f2(); + this->B::f2(); + this->g2(); + this->B::g2(); + this->f3(); + this->B::f3(); + this->g3(); + this->B::g3(); + + a->x0; + a->B::x0; + a->A::x0; + a->B::A::x0; + a->y0; + a->B::y0; + a->A::y0; + a->B::A::y0; + a->x1; + a->B::x1; + a->A::x1; + a->B::A::x1; + a->y1; + a->B::y1; + a->A::y1; + a->B::A::y1; + a->x2; + a->B::x2; + a->y2; + a->B::y2; + a->x3; + a->B::x3; + a->y3; + a->B::y3; + + a->f0(); + a->B::f0(); + a->A::f0(); + a->B::A::f0(); + a->g0(); + a->B::g0(); + a->A::g0(); + a->B::A::g0(); + a->f1(); + a->B::f1(); + a->A::f1(); + a->B::A::f1(); + a->g1(); + a->B::g1(); + a->A::g1(); + a->B::A::g1(); + a->f2(); + a->B::f2(); + a->g2(); + a->B::g2(); + a->f3(); + a->B::f3(); + a->g3(); + a->B::g3(); + + (*this).x0; + (*this).B::x0; + (*this).A::x0; + (*this).B::A::x0; + (*this).y0; + (*this).B::y0; + (*this).A::y0; + (*this).B::A::y0; + (*this).x1; + (*this).B::x1; + (*this).A::x1; + (*this).B::A::x1; + (*this).y1; + (*this).B::y1; + (*this).A::y1; + (*this).B::A::y1; + (*this).x2; + (*this).B::x2; + (*this).y2; + (*this).B::y2; + (*this).x3; + (*this).B::x3; + (*this).y3; + (*this).B::y3; + + (*this).f0(); + (*this).B::f0(); + (*this).A::f0(); + (*this).B::A::f0(); + (*this).g0(); + (*this).B::g0(); + (*this).A::g0(); + (*this).B::A::g0(); + (*this).f1(); + (*this).B::f1(); + (*this).A::f1(); + (*this).B::A::f1(); + (*this).g1(); + (*this).B::g1(); + (*this).A::g1(); + (*this).B::A::g1(); + (*this).f2(); + (*this).B::f2(); + (*this).g2(); + (*this).B::g2(); + (*this).f3(); + (*this).B::f3(); + (*this).g3(); + (*this).B::g3(); + + b.x0; + b.B::x0; + b.A::x0; + b.B::A::x0; + b.y0; + b.B::y0; + b.A::y0; + b.B::A::y0; + b.x1; + b.B::x1; + b.A::x1; + b.B::A::x1; + b.y1; + b.B::y1; + b.A::y1; + b.B::A::y1; + b.x2; + b.B::x2; + b.y2; + b.B::y2; + b.x3; + b.B::x3; + b.y3; + b.B::y3; + + b.f0(); + b.B::f0(); + b.A::f0(); + b.B::A::f0(); + b.g0(); + b.B::g0(); + b.A::g0(); + b.B::A::g0(); + b.f1(); + b.B::f1(); + b.A::f1(); + b.B::A::f1(); + b.g1(); + b.B::g1(); + b.A::g1(); + b.B::A::g1(); + b.f2(); + b.B::f2(); + b.g2(); + b.B::g2(); + b.f3(); + b.B::f3(); + b.g3(); + b.B::g3(); + + // None of the following should be found in the current instantiation. + + new M4; // expected-error{{unknown type name 'M4'}} + new B::M4; // expected-error{{no type named 'M4' in 'B<T>'}} + new A::M4; // expected-error{{no type named 'M4' in 'N0::A'}} + new B::A::M4; // expected-error{{no type named 'M4' in 'N0::A'}} + + x4; // expected-error{{use of undeclared identifier 'x4'}} + B::x4; // expected-error{{no member named 'x4' in 'B<T>'}} + A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + f4(); // expected-error{{use of undeclared identifier 'f4'}} + B::f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + + this->x4; // expected-error{{no member named 'x4' in 'B<T>'}} + this->B::x4; // expected-error{{no member named 'x4' in 'B<T>'}} + this->A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + this->B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + this->f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + this->B::f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + this->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + this->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + + a->x4; // expected-error{{no member named 'x4' in 'B<T>'}} + a->B::x4; // expected-error{{no member named 'x4' in 'B<T>'}} + a->A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + a->B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + a->f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + a->B::f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + a->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + a->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + + // FIXME: An overloaded unary 'operator*' is built for these + // even though the operand is a pointer (to a dependent type). + // Type::isOverloadableType should return false for such cases. + (*this).x4; + (*this).B::x4; + (*this).A::x4; + (*this).B::A::x4; + (*this).f4(); + (*this).B::f4(); + (*this).A::f4(); + (*this).B::A::f4(); + + b.x4; // expected-error{{no member named 'x4' in 'B<T>'}} + b.B::x4; // expected-error{{no member named 'x4' in 'B<T>'}} + b.A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + b.B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + b.f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + b.B::f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + b.A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + b.B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + } + }; +} // namespace N0 + +namespace N1 { + struct A { + template<int I> + void f(); + }; + + template<typename T> + struct B { + template<int I> + void f(); + + A x; + A g(); + + void not_instantiated(B *a, B &b) { + f<0>(); + this->f<0>(); + a->f<0>(); + // FIXME: This should not require 'template'! + (*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + b.f<0>(); + + x.f<0>(); + this->x.f<0>(); + a->x.f<0>(); + // FIXME: This should not require 'template'! + (*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + b.x.f<0>(); + + // FIXME: None of these should require 'template'! + g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + this->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + a->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + (*this).g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + b.g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + } + }; +} // namespace N1 + +namespace N2 { + template<typename T> + struct A { + struct B { + using C = A; + + void not_instantiated(A *a, B *b) { + b->x; // expected-error{{no member named 'x' in 'N2::A::B'}} + b->B::x; // expected-error{{no member named 'x' in 'N2::A::B'}} + a->B::C::x; // expected-error{{no member named 'x' in 'A<T>'}} + } + }; + + void not_instantiated(A *a, B *b) { + b->x; + b->B::x; + a->B::C::x; + } + }; +} + +namespace N3 { + struct A { }; + + template<typename T> + struct B : A { + void not_instantiated() { + // Dependent, lookup context is the current instantiation. + this->operator=(*this); + // Not dependent, the lookup context is A (not the current instantiation). + this->A::operator=(*this); + } + }; +} diff --git a/clang/test/CXX/temp/temp.res/temp.local/p3.cpp b/clang/test/CXX/temp/temp.res/temp.local/p3.cpp index 87589e1e5bcdc8..b9b29d22736e23 100644 --- a/clang/test/CXX/temp/temp.res/temp.local/p3.cpp +++ b/clang/test/CXX/temp/temp.res/temp.local/p3.cpp @@ -16,8 +16,7 @@ template <class T> struct Derived: Base<int>, Base<char> { void g(X0 *t) { t->Derived::Base<T>::f(); t->Base<T>::f(); - t->Base::f(); // expected-error{{member 'Base' found in multiple base classes of different types}} \ - // expected-error{{no member named 'f' in 'X0'}} + t->Base::f(); // expected-error{{member 'Base' found in multiple base classes of different types}} } }; diff --git a/clang/test/CodeGenCXX/mangle.cpp b/clang/test/CodeGenCXX/mangle.cpp index 31467d943840e0..d0800af55c87e8 100644 --- a/clang/test/CodeGenCXX/mangle.cpp +++ b/clang/test/CodeGenCXX/mangle.cpp @@ -1032,10 +1032,6 @@ namespace test51 { template <typename T> decltype(S1<T>().~S1<T>(), S1<T>().~S1<T>()) fun4() {}; template <typename T> - decltype(S1<int>().~S1<T>()) fun5(){}; - template <template <typename T> class U> - decltype(S1<int>().~U<int>()) fun6(){}; - template <typename T> decltype(E().E::~T()) fun7() {} template <template <typename> class U> decltype(X<int>::Y().U<int>::Y::~Y()) fun8() {} @@ -1047,10 +1043,6 @@ namespace test51 { // CHECK-LABEL: @_ZN6test514fun3I2S1IiEiEEDTcldtcvS1_IT0_E_EdnT_EEv template void fun4<int>(); // CHECK-LABEL: @_ZN6test514fun4IiEEDTcmcldtcv2S1IT_E_Edn2S1IS2_EEcldtcvS3__Edn2S1IS2_EEEv - template void fun5<int>(); - // CHECK-LABEL: @_ZN6test514fun5IiEEDTcldtcv2S1IiE_Edn2S1IT_EEEv - template void fun6<S1>(); - // CHECK-LABEL: @_ZN6test514fun6I2S1EEDTcldtcvS1_IiE_EdnT_IiEEEv template void fun7<E>(); // CHECK-LABEL: @_ZN6test514fun7INS_1EEEEDTcldtcvS1__Esr1EEdnT_EEv template void fun8<X>(); diff --git a/clang/test/Index/annotate-nested-name-specifier.cpp b/clang/test/Index/annotate-nested-name-specifier.cpp index a7338db6b05b77..3181497258407f 100644 --- a/clang/test/Index/annotate-nested-name-specifier.cpp +++ b/clang/test/Index/annotate-nested-name-specifier.cpp @@ -132,7 +132,7 @@ struct X8 { struct X9 : X8 { typedef X8 inherited; - void f() { + void f() { inherited::f(); } }; @@ -299,7 +299,7 @@ struct X9 : X8 { // CHECK: Identifier: "type" [77:16 - 77:20] TypeRef=X4::type:70:13 // CHECK: Punctuation: ">" [77:20 - 77:21] MemberRefExpr= // CHECK: Punctuation: "::" [77:21 - 77:23] MemberRefExpr= -// CHECK: Identifier: "g" [77:23 - 77:24] MemberRefExpr= +// CHECK: Identifier: "g" [77:23 - 77:24] OverloadedDeclRef= // CHECK: Punctuation: "(" [77:24 - 77:25] CallExpr= // CHECK: Identifier: "t" [77:25 - 77:26] DeclRefExpr=t:74:12 // CHECK: Punctuation: ")" [77:26 - 77:27] CallExpr= diff --git a/clang/test/SemaCXX/member-expr.cpp b/clang/test/SemaCXX/member-expr.cpp index 75c9ef0caa2e00..0596e40f6c2f6a 100644 --- a/clang/test/SemaCXX/member-expr.cpp +++ b/clang/test/SemaCXX/member-expr.cpp @@ -40,8 +40,8 @@ namespace C { } void test2(X *xp) { - xp->::i = 7; // expected-error{{qualified member access refers to a member in the global namespace}} - xp->C::i = 7; // expected-error{{qualified member access refers to a member in namespace 'C'}} + xp->::i = 7; // expected-error{{'i' is not a member of class 'X'}} + xp->C::i = 7; // expected-error{{'C::i' is not a member of class 'X'}} } diff --git a/clang/test/SemaTemplate/instantiate-function-1.cpp b/clang/test/SemaTemplate/instantiate-function-1.cpp index ceef2743774805..a4967264c654b7 100644 --- a/clang/test/SemaTemplate/instantiate-function-1.cpp +++ b/clang/test/SemaTemplate/instantiate-function-1.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify %s template<typename T, typename U> struct X0 { - void f(T x, U y) { + void f(T x, U y) { (void)(x + y); // expected-error{{invalid operands}} } }; @@ -41,7 +41,7 @@ template <typename T> struct X4 { T f() const { return; // expected-error{{non-void function 'f' should return a value}} } - + T g() const { return 1; // expected-error{{void function 'g' should not return a value}} } @@ -64,7 +64,7 @@ template<typename T, typename U, typename V> struct X6 { // IfStmt if (t > 0) return u; - else { + else { if (t < 0) return v; // expected-error{{cannot initialize return object of type}} } @@ -131,12 +131,12 @@ template<typename T> struct Member0 { t; t.f; t->f; - + T* tp; tp.f; // expected-error{{member reference base type 'T *' is not a structure or union}} tp->f; - this->f; + this->f; // expected-error{{reference to non-static member function must be called}} this.f; // expected-error{{member reference base type 'Member0<T> *' is not a structure or union}} } }; @@ -239,11 +239,11 @@ namespace PR9880 { static yes_tag check(char[sizeof(&U::luaIndex)]); enum { value = sizeof(check<T>(0)) == sizeof(yes_tag) }; }; - + class SomeClass { public: int luaIndex(lua_State* L); }; - + int i = HasIndexMetamethod<SomeClass>::value; } diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 87774b00956a5a..c08deb903f129b 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -1569,8 +1569,9 @@ TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) { matches("class Y { void x() { y; } int y; };", memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };", memberExpr(isArrow()))); - EXPECT_TRUE(matches("template <class T> class Y { void x() { this->m; } };", - cxxDependentScopeMemberExpr(isArrow()))); + EXPECT_TRUE( + matches("template <class T> class Y { void x() { this->m; } int m; };", + memberExpr(isArrow()))); EXPECT_TRUE( notMatches("template <class T> class Y { void x() { (*this).m; } };", cxxDependentScopeMemberExpr(isArrow()))); >From 0ce313ab7562cdc7a48ea039ead85f425cfa5f7a Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Thu, 25 Apr 2024 20:20:46 -0400 Subject: [PATCH 2/2] [Clang][Sema] Fix arrow operator for expressions with non-pointer type that is the current instantiation --- clang/lib/Sema/SemaExprMember.cpp | 4 +- .../temp.res/temp.dep/temp.dep.type/p4.cpp | 48 ++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 14dde1bff8fbc6..157ca23f6dd638 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1004,7 +1004,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, bool SuppressQualifierCheck, ActOnMemberAccessExtraArgs *ExtraArgs) { assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid"); - if (R.wasNotFoundInCurrentInstantiation()) + if (R.wasNotFoundInCurrentInstantiation() || + (IsArrow && !BaseExprType->isPointerType() && + BaseExprType->isDependentType())) return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS, TemplateKWLoc, FirstQualifierInScope, R.getLookupNameInfo(), TemplateArgs); diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp index b1d2859be863aa..2dad86c753e6fd 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp @@ -439,7 +439,7 @@ namespace N2 { a->B::C::x; } }; -} +} // namespace N2 namespace N3 { struct A { }; @@ -453,4 +453,48 @@ namespace N3 { this->A::operator=(*this); } }; -} +} // namespace N3 + +namespace N3 { + template<typename T> + struct A { + void not_instantiated(A a, A<T> b, T c) { + a->x; + b->x; + c->x; + } + + void instantiated(A a, A<T> b, T c) { + a->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}} + // expected-error@-1 {{no member named 'x' in 'N3::A<int>'}} + b->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}} + // expected-error@-1 {{no member named 'x' in 'N3::A<int>'}} + c->x; // expected-error {{member reference type 'int' is not a pointer}} + } + }; + + template void A<int>::instantiated(A<int>, A<int>, int); // expected-note {{in instantiation of}} + + struct B { + int x; + }; + + template<typename T> + struct C { + B *operator->(); + + void not_instantiated(C a, C<T> b, T c) { + a->x; + b->x; + c->x; + } + + void instantiated(C a, C<T> b, T c) { + a->x; + b->x; + c->x; // expected-error {{member reference type 'int' is not a pointer}} + } + }; + + template void C<int>::instantiated(C, C, int); // expected-note {{in instantiation of}} +} // namespace N3 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits