diff options
author | Tom Stellard <tstellar@redhat.com> | 2018-10-26 17:32:52 +0000 |
---|---|---|
committer | Tom Stellard <tstellar@redhat.com> | 2018-10-26 17:32:52 +0000 |
commit | ea49dcd184eda2426a4042631aa3cbbc5fa3b6df (patch) | |
tree | b5bba5a61453e98375c3d473e9c4f81da023df8d | |
parent | 9e981fb6e135e1ac0a900b76c8798c1cf510248d (diff) | |
download | llvm-ea49dcd184eda2426a4042631aa3cbbc5fa3b6df.zip llvm-ea49dcd184eda2426a4042631aa3cbbc5fa3b6df.tar.gz llvm-ea49dcd184eda2426a4042631aa3cbbc5fa3b6df.tar.bz2 |
Merging r341775:
------------------------------------------------------------------------
r341775 | rsmith | 2018-09-09 22:32:13 -0700 (Sun, 09 Sep 2018) | 2 lines
Part of PR33222: defer enforcing return type mismatch for dependent
friend function declarations of class templates.
------------------------------------------------------------------------
llvm-svn: 345409
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 79 | ||||
-rw-r--r-- | clang/test/SemaCXX/friend.cpp | 23 |
3 files changed, 85 insertions, 19 deletions
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b1077c6..2365b59 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1950,6 +1950,8 @@ public: FunctionDecl *NewFD, LookupResult &Previous, bool IsMemberSpecialization); bool shouldLinkDependentDeclWithPrevious(Decl *D, Decl *OldDecl); + bool canFullyTypeCheckRedeclaration(ValueDecl *NewD, ValueDecl *OldD, + QualType NewT, QualType OldT); void CheckMain(FunctionDecl *FD, const DeclSpec &D); void CheckMSVCRTEntryPoint(FunctionDecl *FD); Attr *getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD, bool IsDefinition); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index b92d76a..cdf412a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3258,8 +3258,8 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, ? New->getTypeSourceInfo()->getType()->castAs<FunctionType>() : NewType)->getReturnType(); if (!Context.hasSameType(OldDeclaredReturnType, NewDeclaredReturnType) && - !((NewQType->isDependentType() || OldQType->isDependentType()) && - New->isLocalExternDecl())) { + canFullyTypeCheckRedeclaration(New, Old, NewDeclaredReturnType, + OldDeclaredReturnType)) { QualType ResQT; if (NewDeclaredReturnType->isObjCObjectPointerType() && OldDeclaredReturnType->isObjCObjectPointerType()) @@ -3427,13 +3427,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, if (OldQTypeForComparison == NewQType) return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld); - if ((NewQType->isDependentType() || OldQType->isDependentType()) && - New->isLocalExternDecl()) { - // It's OK if we couldn't merge types for a local function declaraton - // if either the old or new type is dependent. We'll merge the types - // when we instantiate the function. + // If the types are imprecise (due to dependent constructs in friends or + // local extern declarations), it's OK if they differ. We'll check again + // during instantiation. + if (!canFullyTypeCheckRedeclaration(New, Old, NewQType, OldQType)) return false; - } // Fall through for conflicting redeclarations and redefinitions. } @@ -9336,6 +9334,39 @@ Attr *Sema::getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD, } return nullptr; } + +/// Determines if we can perform a correct type check for \p D as a +/// redeclaration of \p PrevDecl. If not, we can generally still perform a +/// best-effort check. +/// +/// \param NewD The new declaration. +/// \param OldD The old declaration. +/// \param NewT The portion of the type of the new declaration to check. +/// \param OldT The portion of the type of the old declaration to check. +bool Sema::canFullyTypeCheckRedeclaration(ValueDecl *NewD, ValueDecl *OldD, + QualType NewT, QualType OldT) { + if (!NewD->getLexicalDeclContext()->isDependentContext()) + return true; + + // For dependently-typed local extern declarations and friends, we can't + // perform a correct type check in general until instantiation: + // + // int f(); + // template<typename T> void g() { T f(); } + // + // (valid if g() is only instantiated with T = int). + if (NewT->isDependentType() && + (NewD->isLocalExternDecl() || NewD->getFriendObjectKind())) + return false; + + // Similarly, if the previous declaration was a dependent local extern + // declaration, we don't really know its type yet. + if (OldT->isDependentType() && OldD->isLocalExternDecl()) + return false; + + return true; +} + /// Checks if the new declaration declared in dependent context must be /// put in the same redeclaration chain as the specified declaration. /// @@ -9346,20 +9377,30 @@ Attr *Sema::getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD, /// belongs to. /// bool Sema::shouldLinkDependentDeclWithPrevious(Decl *D, Decl *PrevDecl) { - // Any declarations should be put into redeclaration chains except for - // friend declaration in a dependent context that names a function in - // namespace scope. + if (!D->getLexicalDeclContext()->isDependentContext()) + return true; + + // Don't chain dependent friend function definitions until instantiation, to + // permit cases like // - // This allows to compile code like: + // void func(); + // template<typename T> class C1 { friend void func() {} }; + // template<typename T> class C2 { friend void func() {} }; // - // void func(); - // template<typename T> class C1 { friend void func() { } }; - // template<typename T> class C2 { friend void func() { } }; + // ... which is valid if only one of C1 and C2 is ever instantiated. // - // This code snippet is a valid code unless both templates are instantiated. - return !(D->getLexicalDeclContext()->isDependentContext() && - D->getDeclContext()->isFileContext() && - D->getFriendObjectKind() != Decl::FOK_None); + // FIXME: This need only apply to function definitions. For now, we proxy + // this by checking for a file-scope function. We do not want this to apply + // to friend declarations nominating member functions, because that gets in + // the way of access checks. + if (D->getFriendObjectKind() && D->getDeclContext()->isFileContext()) + return false; + + auto *VD = dyn_cast<ValueDecl>(D); + auto *PrevVD = dyn_cast<ValueDecl>(PrevDecl); + return !VD || !PrevVD || + canFullyTypeCheckRedeclaration(VD, PrevVD, VD->getType(), + PrevVD->getType()); } namespace MultiVersioning { diff --git a/clang/test/SemaCXX/friend.cpp b/clang/test/SemaCXX/friend.cpp index 1f64ba6..61e9692 100644 --- a/clang/test/SemaCXX/friend.cpp +++ b/clang/test/SemaCXX/friend.cpp @@ -388,3 +388,26 @@ namespace default_arg { friend void f(void *p = 0) {} // expected-error {{must be the only}} }; } + +namespace PR33222 { + int f(); + template<typename T> struct X { + friend T f(); + }; + X<int> xi; + + int g(); // expected-note {{previous}} + template<typename T> struct Y { + friend T g(); // expected-error {{return type}} + }; + Y<float> yf; // expected-note {{instantiation}} + + int h(); + template<typename T> struct Z { + // FIXME: The note here should point at the non-friend declaration, not the + // instantiation in Z<int>. + friend T h(); // expected-error {{return type}} expected-note {{previous}} + }; + Z<int> zi; + Z<float> zf; // expected-note {{instantiation}} +} |