Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1781,11 +1781,32 @@ 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). + /// \name Definition and body checks + /// + /// 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 from viewpoint of ODR checks. + /// - 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). + /// + /// Depending on whether the redeclaration chain contains a definition and of + /// what kind, the same classification applies to the function represented by + /// a set of redeclarations. + /// + /// \{ + + /// 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 { @@ -1793,13 +1814,16 @@ return hasBody(Definition); } - /// hasTrivialBody - Returns whether the function has a trivial body that does - /// not require any specific codegen. - bool hasTrivialBody() const; - - /// isDefined - 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 only difference to isOdrDefined is that this function does not take + /// into account functions where body is not instantiated because it is not + /// used. + /// + /// 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 { @@ -1807,6 +1831,48 @@ return isDefined(Definition); } + /// Returns true if the function is defined in the sense of ODR checks. + /// + bool isOdrDefined(const FunctionDecl *&Definition) const; + bool isOdrDefined() const { + const FunctionDecl* Definition; + return isOdrDefined(Definition); + } + + /// Returns whether this specific declaration of the function is also a + /// definition that does not contain uninstantiated body. + /// + /// This does not determine whether the function has been defined (e.g., in a + /// previous definition); for that information, use isDefined. + /// + bool isThisDeclarationADefinition() const { + return IsDeleted || IsDefaulted || Body || IsLateTemplateParsed || + hasDefiningAttr(); + } + + /// Returns whether this specific declaration of the function has a body. + /// + bool doesThisDeclarationHaveABody() const { + return Body || IsLateTemplateParsed; + } + + /// Returns true if this declaration is a definition is the sense of ODR + /// checks. + /// + bool isThisDeclarationAnOdrDefinition() const { + if (isThisDeclarationADefinition()) + return true; + if (FunctionDecl *Original = getInstantiatedFromMemberFunction()) + return Original->isOdrDefined(); + return false; + } + + /// \} + + /// hasTrivialBody - Returns whether the function has a trivial body that does + /// not require any specific codegen. + bool hasTrivialBody() const; + /// \brief Get the definition for this declaration. FunctionDecl *getDefinition() { const FunctionDecl *Definition; @@ -1832,23 +1898,6 @@ return getBody(Definition); } - /// isThisDeclarationADefinition - Returns whether this specific - /// declaration of the function is also a definition. This does not - /// determine whether the function has been defined (e.g., in a - /// previous definition); for that information, use isDefined. Note - /// that this returns false for a defaulted function unless that function - /// has been implicitly defined (possibly as deleted). - bool isThisDeclarationADefinition() const { - return IsDeleted || Body || IsLateTemplateParsed; - } - - /// doesThisDeclarationHaveABody - Returns whether this specific - /// declaration of the function has a body - that is, if it is a non- - /// deleted definition. - bool doesThisDeclarationHaveABody() const { - return Body || IsLateTemplateParsed; - } - void setBody(Stmt *B); void setLazyBody(uint64_t Offset) { Body = Offset; } Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -2533,8 +2533,18 @@ bool FunctionDecl::isDefined(const FunctionDecl *&Definition) const { for (auto I : redecls()) { - if (I->IsDeleted || I->IsDefaulted || I->Body || I->IsLateTemplateParsed || - I->hasDefiningAttr()) { + if (I->isThisDeclarationADefinition()) { + Definition = I->IsDeleted ? I->getCanonicalDecl() : I; + return true; + } + } + + return false; +} + +bool FunctionDecl::isOdrDefined(const FunctionDecl *&Definition) const { + for (auto I : redecls()) { + if (I->isThisDeclarationAnOdrDefinition()) { Definition = I->IsDeleted ? I->getCanonicalDecl() : I; return true; } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -11692,8 +11692,12 @@ SkipBodyInfo *SkipBody) { const FunctionDecl *Definition = EffectiveDefinition; if (!Definition) - if (!FD->isDefined(Definition)) + if (!FD->isOdrDefined(Definition)) return; + assert(Definition); + + if (FD == Definition) + return; if (canRedefineFunction(Definition, getLangOpts())) return; Index: test/SemaCXX/cxx0x-cursory-default-delete.cpp =================================================================== --- test/SemaCXX/cxx0x-cursory-default-delete.cpp +++ test/SemaCXX/cxx0x-cursory-default-delete.cpp @@ -136,13 +136,13 @@ }; struct DefaultDelete { - DefaultDelete() = default; // expected-note {{previous declaration is here}} + DefaultDelete() = default; // expected-note {{previous definition is here}} DefaultDelete() = delete; // expected-error {{constructor cannot be redeclared}} - ~DefaultDelete() = default; // expected-note {{previous declaration is here}} + ~DefaultDelete() = default; // expected-note {{previous definition is here}} ~DefaultDelete() = delete; // expected-error {{destructor cannot be redeclared}} - DefaultDelete &operator=(const DefaultDelete &) = default; // expected-note {{previous declaration is here}} + DefaultDelete &operator=(const DefaultDelete &) = default; // expected-note {{previous definition is here}} DefaultDelete &operator=(const DefaultDelete &) = delete; // expected-error {{class member cannot be redeclared}} }; Index: test/SemaCXX/friend2.cpp =================================================================== --- test/SemaCXX/friend2.cpp +++ test/SemaCXX/friend2.cpp @@ -101,6 +101,34 @@ 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 {