From 3b7400d6f495be0d67367c83922737d75a2140e6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 10 Sep 2018 05:32:13 +0000 Subject: [PATCH] Part of PR33222: defer enforcing return type mismatch for dependent friend function declarations of class templates. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341775 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 2 + lib/Sema/SemaDecl.cpp | 79 +++++++++++++++++++++++++++++---------- test/SemaCXX/friend.cpp | 23 ++++++++++++ 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 9556fdab3a..c6a0a25562 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1956,6 +1956,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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c705b4920e..538978799e 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3254,8 +3254,8 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, ? New->getTypeSourceInfo()->getType()->castAs() : 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()) @@ -3423,13 +3423,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. } @@ -9300,6 +9298,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 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. /// @@ -9310,20 +9341,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 class C1 { friend void func() {} }; + // template class C2 { friend void func() {} }; // - // void func(); - // template class C1 { friend void func() { } }; - // template 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(D); + auto *PrevVD = dyn_cast(PrevDecl); + return !VD || !PrevVD || + canFullyTypeCheckRedeclaration(VD, PrevVD, VD->getType(), + PrevVD->getType()); } namespace MultiVersioning { diff --git a/test/SemaCXX/friend.cpp b/test/SemaCXX/friend.cpp index 1f64ba609b..61e96922f6 100644 --- a/test/SemaCXX/friend.cpp +++ b/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 struct X { + friend T f(); + }; + X xi; + + int g(); // expected-note {{previous}} + template struct Y { + friend T g(); // expected-error {{return type}} + }; + Y yf; // expected-note {{instantiation}} + + int h(); + template struct Z { + // FIXME: The note here should point at the non-friend declaration, not the + // instantiation in Z. + friend T h(); // expected-error {{return type}} expected-note {{previous}} + }; + Z zi; + Z zf; // expected-note {{instantiation}} +} -- 2.40.0