From: Serge Pavlov Date: Thu, 1 Mar 2018 07:04:11 +0000 (+0000) Subject: Function definition may have uninstantiated body X-Git-Tag: llvmorg-7.0.0-rc1~11672 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e6e534ca22fb38ca06b92d87c7847920f5bf3fe9;p=platform%2Fupstream%2Fllvm.git Function definition may have uninstantiated body Current implementation of `FunctionDecl::isDefined` does not take into account redeclarations that do not have bodies, but the bodies can be instantiated from corresponding templated definition. This behavior does not allow to detect function redefinition in the cases where friend functions is defined in class templates. For instance, the code: ``` template struct X { friend void f() {} }; X xi; void f() {} ``` compiles successfully but must fail due to redefinition of `f`. The declaration of the friend `f` is created when the containing template `X` is instantiated, but it does not have a body as per 14.5.4p4 because `f` is not odr-used. With this change the function `Sema::CheckForFunctionRedefinition` considers functions with uninstantiated bodies as definitions. Differential Revision: https://reviews.llvm.org/D30170 llvm-svn: 326419 --- diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 1a8f656..5b5ad34 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 a176964..62fd020 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 struct C20 { friend void func_20() {} }; + // C20 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 d87f9db..800956d 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 d1d4b62..8eacdeb 100644 --- a/clang/test/SemaCXX/friend2.cpp +++ b/clang/test/SemaCXX/friend2.cpp @@ -101,6 +101,34 @@ template 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 struct C20 { + friend void func_20() {} // expected-note{{previous definition is here}} +}; +C20 c20i; +void func_20() {} // expected-error{{redefinition of 'func_20'}} + +template struct C21a { + friend void func_21() {} // expected-note{{previous definition is here}} +}; +template struct C21b { + friend void func_21() {} // expected-error{{redefinition of 'func_21'}} +}; +C21a c21ai; +C21b c21bi; // expected-note{{in instantiation of template class 'C21b' requested here}} + +template struct C22a { + friend void func_22() {} // expected-note{{previous definition is here}} +}; +template struct C22b { + friend void func_22(); +}; +C22a c22ai; +C22b c22bi; +void func_22() {} // expected-error{{redefinition of 'func_22'}} + + namespace pr22307 {