diff options
| -rw-r--r-- | clang/include/clang/AST/Decl.h | 35 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 31 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 57 | ||||
| -rw-r--r-- | clang/test/SemaCXX/friend2.cpp | 28 |
4 files changed, 100 insertions, 51 deletions
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 1a8f6560e44..5b5ad34b0d4 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1925,11 +1925,25 @@ public: SourceRange getSourceRange() const override LLVM_READONLY; - /// \brief Returns true if the function has a body (definition). The - /// function body might be in any of the (re-)declarations of this - /// function. The variant that accepts a FunctionDecl pointer will - /// set that function declaration to the actual declaration - /// containing the body (if there is one). + // Function definitions. + // + // A function declaration may be: + // - a non defining declaration, + // - a definition. A function may be defined because: + // - it has a body, or will have it in the case of late parsing. + // - it has an uninstantiated body. The body does not exist because the + // function is not used yet, but the declaration is considered a + // definition and does not allow other definition of this function. + // - it does not have a user specified body, but it does not allow + // redefinition, because it is deleted/defaulted or is defined through + // some other mechanism (alias, ifunc). + + /// Returns true if the function has a body. + /// + /// The function body might be in any of the (re-)declarations of this + /// function. The variant that accepts a FunctionDecl pointer will set that + /// function declaration to the actual declaration containing the body (if + /// there is one). bool hasBody(const FunctionDecl *&Definition) const; bool hasBody() const override { @@ -1941,9 +1955,11 @@ public: /// specific codegen. bool hasTrivialBody() const; - /// Returns true if the function is defined at all, including a deleted - /// definition. Except for the behavior when the function is deleted, behaves - /// like hasBody. + /// Returns true if the function has a definition that does not need to be + /// instantiated. + /// + /// The variant that accepts a FunctionDecl pointer will set that function + /// declaration to the declaration that is a definition (if there is one). bool isDefined(const FunctionDecl *&Definition) const; virtual bool isDefined() const { @@ -1985,8 +2001,7 @@ public: IsLateTemplateParsed || WillHaveBody || hasDefiningAttr(); } - /// Returns whether this specific declaration of the function has a body - - /// that is, if it is a non-deleted definition. + /// Returns whether this specific declaration of the function has a body. bool doesThisDeclarationHaveABody() const { return Body || IsLateTemplateParsed; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a176964fc2f..62fd0207715 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12283,9 +12283,36 @@ Sema::CheckForFunctionRedefinition(FunctionDecl *FD, const FunctionDecl *EffectiveDefinition, SkipBodyInfo *SkipBody) { const FunctionDecl *Definition = EffectiveDefinition; + if (!Definition && !FD->isDefined(Definition) && !FD->isCXXClassMember()) { + // If this is a friend function defined in a class template, it does not + // have a body until it is used, nevertheless it is a definition, see + // [temp.inst]p2: + // + // ... for the purpose of determining whether an instantiated redeclaration + // is valid according to [basic.def.odr] and [class.mem], a declaration that + // corresponds to a definition in the template is considered to be a + // definition. + // + // The following code must produce redefinition error: + // + // template<typename T> struct C20 { friend void func_20() {} }; + // C20<int> c20i; + // void func_20() {} + // + for (auto I : FD->redecls()) { + if (I != FD && !I->isInvalidDecl() && + I->getFriendObjectKind() != Decl::FOK_None) { + if (FunctionDecl *Original = I->getInstantiatedFromMemberFunction()) { + if (Original->isThisDeclarationADefinition()) { + Definition = I; + break; + } + } + } + } + } if (!Definition) - if (!FD->isDefined(Definition)) - return; + return; if (canRedefineFunction(Definition, getLangOpts())) return; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index d87f9dbbbea..800956dd535 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1806,45 +1806,24 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, // apply to non-template function declarations and definitions also apply // to these implicit definitions. if (D->isThisDeclarationADefinition()) { - // Check for a function body. - const FunctionDecl *Definition = nullptr; - if (Function->isDefined(Definition) && - Definition->getTemplateSpecializationKind() == TSK_Undeclared) { - SemaRef.Diag(Function->getLocation(), diag::err_redefinition) - << Function->getDeclName(); - SemaRef.Diag(Definition->getLocation(), diag::note_previous_definition); - } - // Check for redefinitions due to other instantiations of this or - // a similar friend function. - else for (auto R : Function->redecls()) { - if (R == Function) - continue; - - // If some prior declaration of this function has been used, we need - // to instantiate its definition. - if (!QueuedInstantiation && R->isUsed(false)) { - if (MemberSpecializationInfo *MSInfo = - Function->getMemberSpecializationInfo()) { - if (MSInfo->getPointOfInstantiation().isInvalid()) { - SourceLocation Loc = R->getLocation(); // FIXME - MSInfo->setPointOfInstantiation(Loc); - SemaRef.PendingLocalImplicitInstantiations.push_back( - std::make_pair(Function, Loc)); - QueuedInstantiation = true; - } - } - } - - // If some prior declaration of this function was a friend with an - // uninstantiated definition, reject it. - if (R->getFriendObjectKind()) { - if (const FunctionDecl *RPattern = - R->getTemplateInstantiationPattern()) { - if (RPattern->isDefined(RPattern)) { - SemaRef.Diag(Function->getLocation(), diag::err_redefinition) - << Function->getDeclName(); - SemaRef.Diag(R->getLocation(), diag::note_previous_definition); - break; + SemaRef.CheckForFunctionRedefinition(Function); + if (!Function->isInvalidDecl()) { + for (auto R : Function->redecls()) { + if (R == Function) + continue; + + // If some prior declaration of this function has been used, we need + // to instantiate its definition. + if (!QueuedInstantiation && R->isUsed(false)) { + if (MemberSpecializationInfo *MSInfo = + Function->getMemberSpecializationInfo()) { + if (MSInfo->getPointOfInstantiation().isInvalid()) { + SourceLocation Loc = R->getLocation(); // FIXME + MSInfo->setPointOfInstantiation(Loc); + SemaRef.PendingLocalImplicitInstantiations.push_back( + std::make_pair(Function, Loc)); + QueuedInstantiation = true; + } } } } diff --git a/clang/test/SemaCXX/friend2.cpp b/clang/test/SemaCXX/friend2.cpp index d1d4b628ba2..8eacdeb19c6 100644 --- a/clang/test/SemaCXX/friend2.cpp +++ b/clang/test/SemaCXX/friend2.cpp @@ -101,6 +101,34 @@ template<typename T> class C12 { friend void func_12(int x = 0); // expected-error{{friend declaration specifying a default argument must be the only declaration}} }; +// Friend function with uninstantiated body is still a definition. + +template<typename T> struct C20 { + friend void func_20() {} // expected-note{{previous definition is here}} +}; +C20<int> c20i; +void func_20() {} // expected-error{{redefinition of 'func_20'}} + +template<typename T> struct C21a { + friend void func_21() {} // expected-note{{previous definition is here}} +}; +template<typename T> struct C21b { + friend void func_21() {} // expected-error{{redefinition of 'func_21'}} +}; +C21a<int> c21ai; +C21b<int> c21bi; // expected-note{{in instantiation of template class 'C21b<int>' requested here}} + +template<typename T> struct C22a { + friend void func_22() {} // expected-note{{previous definition is here}} +}; +template<typename T> struct C22b { + friend void func_22(); +}; +C22a<int> c22ai; +C22b<int> c22bi; +void func_22() {} // expected-error{{redefinition of 'func_22'}} + + namespace pr22307 { |

