I believe the case you're describing is one like this: struct A { virtual void f() = delete; }; template<typename T> struct B : A { virtual void f() = delete; }; B<int> b;
Fixed in llvmorg-11-init-5618-g9975dc38bf7. On Tue, 10 Mar 2020 at 18:01, Hubert Tong via cfe-commits < cfe-commits@lists.llvm.org> wrote: > On Tue, Mar 10, 2020 at 7:35 PM Richard Smith <rich...@metafoo.co.uk> > wrote: > >> Should be fixed in llvmorg-11-init-5426-g4cba668ac13. >> > Thanks. We're also seeing a failure on valid code where a template class > explicitly deletes a function that is an override of an explicitly deleted > virtual function in a (non-templated) base class. > > <stdin>:7:16: error: non-deleted function 'f' cannot override a deleted > function > virtual void f() = delete; > ^ > <stdin>:10:8: note: in instantiation of template class 'B<int>' requested > here > B<int> b() { return {}; } > ^ > <stdin>:2:16: note: overridden virtual function is here > virtual void f() = delete; > ^ > 1 error generated. > > >> >> On Sat, 7 Mar 2020 at 08:05, Hubert Tong via cfe-commits < >> cfe-commits@lists.llvm.org> wrote: >> >>> Following this commit, the error recovery for invalid cases that >>> explicitly define (out-of-line) a member function template as deleted and >>> attempts to instantiate said function appears broken. >>> >>> <stdin>:4:35: error: deleted definition must be first declaration >>> template <typename> void A::f() = delete; >>> ^ >>> <stdin>:2:35: note: previous declaration is here >>> template <typename> static void f(); >>> ^ >>> clang: >>> /src_d052a578de58cbbb638cbe2dba05242d1ff443b9/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp:4288: >>> void clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation, >>> clang::FunctionDecl *, bool, bool, bool): Assertion `(Pattern || >>> PatternDecl->isDefaulted() || PatternDecl->hasSkippedBody()) && "unexpected >>> kind of function template definition"' failed. >>> Stack dump: >>> 0. Program arguments: >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang -cc1 -std=c++11 >>> -xc++ - >>> 1. <stdin>:5:26: current parser token ';' >>> #0 0x00003fff7fe6a024 PrintStackTraceSignalHandler(void*) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn+0x1ea024) >>> #1 0x00003fff7fe670c8 llvm::sys::RunSignalHandlers() >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn+0x1e70c8) >>> #2 0x00003fff7fe6a49c SignalHandler(int) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn+0x1ea49c) >>> #3 0x00003fff82030478 0x478 abort >>> #4 0x00003fff82030478 >>> #5 0x00003fff82030478 __assert_fail_base (+0x478) >>> #6 0x00003fff7e0a1f94 __assert_fail (/lib64/libc.so.6+0x41f94) >>> #7 0x00003fff7e0955d4 >>> clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation, >>> clang::FunctionDecl*, bool, bool, bool) (/lib64/libc.so.6+0x355d4) >>> #8 0x00003fff7e0956c4 >>> clang::Sema::ActOnExplicitInstantiation(clang::Scope*, >>> clang::SourceLocation, clang::SourceLocation, clang::Declarator&) >>> (/lib64/libc.so.6+0x356c4) >>> #9 0x00003fff7c28d604 >>> clang::Parser::ParseDeclarationAfterDeclaratorAndAttributes(clang::Declarator&, >>> clang::Parser::ParsedTemplateInfo const&, clang::Parser::ForRangeInit*) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn+0x8ad604) >>> #10 0x00003fff7c15c2b0 >>> clang::Parser::ParseDeclarationAfterDeclarator(clang::Declarator&, >>> clang::Parser::ParsedTemplateInfo const&) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn+0x77c2b0) >>> #11 0x00003fff7c4cc8f8 >>> clang::Parser::ParseSingleDeclarationAfterTemplate(clang::DeclaratorContext, >>> clang::Parser::ParsedTemplateInfo const&, clang::ParsingDeclRAIIObject&, >>> clang::SourceLocation&, clang::ParsedAttributes&, clang::AccessSpecifier) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x4c8f8) >>> #12 0x00003fff7c4cdf48 >>> clang::Parser::ParseExplicitInstantiation(clang::DeclaratorContext, >>> clang::SourceLocation, clang::SourceLocation, clang::SourceLocation&, >>> clang::ParsedAttributes&, clang::AccessSpecifier) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x4df48) >>> #13 0x00003fff7c57c1f0 >>> clang::Parser::ParseDeclarationStartingWithTemplate(clang::DeclaratorContext, >>> clang::SourceLocation&, clang::ParsedAttributes&, clang::AccessSpecifier) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0xfc1f0) >>> #14 0x00003fff7c57a6c0 >>> clang::Parser::ParseDeclaration(clang::DeclaratorContext, >>> clang::SourceLocation&, clang::Parser::ParsedAttributesWithRange&, >>> clang::SourceLocation*) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0xfa6c0) >>> #15 0x00003fff7c57a4f8 >>> clang::Parser::ParseExternalDeclaration(clang::Parser::ParsedAttributesWithRange&, >>> clang::ParsingDeclSpec*) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0xfa4f8) >>> #16 0x00003fff7c4c5db0 >>> clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, >>> bool) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x45db0) >>> #17 0x00003fff7c58fffc clang::ParseAST(clang::Sema&, bool, bool) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x10fffc) >>> #18 0x00003fff7c58def4 clang::ASTFrontendAction::ExecuteAction() >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x10def4) >>> #19 0x00003fff7c4b01e0 clang::FrontendAction::Execute() >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x301e0) >>> #20 0x00003fff7e93d57c >>> clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn+0x10d57c) >>> #21 0x00003fff7e93cbf0 >>> clang::ExecuteCompilerInvocation(clang::CompilerInstance*) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn+0x10cbf0) >>> #22 0x00003fff7e8d5bd4 cc1_main(llvm::ArrayRef<char const*>, char >>> const*, void*) >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn+0xa5bd4) >>> #23 0x00003fff7e8042f0 main >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontendTool.so.10svn+0x42f0) >>> #24 0x0000000010012594 generic_start_main.isra.0 >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang+0x10012594) >>> #25 0x000000001000f37c __libc_start_main >>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang+0x1000f37c) >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn(+0x1ea024)[0x3fff7fe6a024] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn(_ZN4llvm3sys17RunSignalHandlersEv+0xc8)[0x3fff7fe670c8] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn(+0x1ea49c)[0x3fff7fe6a49c] >>> [0x3fff82030478] >>> /lib64/libc.so.6(abort+0x2b4)[0x3fff7e0a1f94] >>> /lib64/libc.so.6(+0x355d4)[0x3fff7e0955d4] >>> /lib64/libc.so.6(__assert_fail+0x64)[0x3fff7e0956c4] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn(_ZN5clang4Sema29InstantiateFunctionDefinitionENS_14SourceLocationEPNS_12FunctionDeclEbbb+0x1244)[0x3fff7c28d604] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn(_ZN5clang4Sema26ActOnExplicitInstantiationEPNS_5ScopeENS_14SourceLocationES3_RNS_10DeclaratorE+0x2290)[0x3fff7c15c2b0] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser44ParseDeclarationAfterDeclaratorAndAttributesERNS_10DeclaratorERKNS0_18ParsedTemplateInfoEPNS0_12ForRangeInitE+0xf8)[0x3fff7c4cc8f8] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser31ParseDeclarationAfterDeclaratorERNS_10DeclaratorERKNS0_18ParsedTemplateInfoE+0x98)[0x3fff7c4cdf48] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser35ParseSingleDeclarationAfterTemplateENS_17DeclaratorContextERKNS0_18ParsedTemplateInfoERNS_21ParsingDeclRAIIObjectERNS_14SourceLocationERNS_16ParsedAttributesENS_15AccessSpecifierE+0x950)[0x3fff7c57c1f0] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser26ParseExplicitInstantiationENS_17DeclaratorContextENS_14SourceLocationES2_RS2_RNS_16ParsedAttributesENS_15AccessSpecifierE+0x80)[0x3fff7c57a6c0] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser36ParseDeclarationStartingWithTemplateENS_17DeclaratorContextERNS_14SourceLocationERNS_16ParsedAttributesENS_15AccessSpecifierE+0x158)[0x3fff7c57a4f8] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser16ParseDeclarationENS_17DeclaratorContextERNS_14SourceLocationERNS0_25ParsedAttributesWithRangeEPS2_+0x350)[0x3fff7c4c5db0] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser24ParseExternalDeclarationERNS0_25ParsedAttributesWithRangeEPNS_15ParsingDeclSpecE+0x2bc)[0x3fff7c58fffc] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser17ParseTopLevelDeclERNS_9OpaquePtrINS_12DeclGroupRefEEEb+0x614)[0x3fff7c58def4] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang8ParseASTERNS_4SemaEbb+0x2c0)[0x3fff7c4b01e0] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn(_ZN5clang17ASTFrontendAction13ExecuteActionEv+0xdc)[0x3fff7e93d57c] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn(_ZN5clang14FrontendAction7ExecuteEv+0x150)[0x3fff7e93cbf0] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn(_ZN5clang16CompilerInstance13ExecuteActionERNS_14FrontendActionE+0x714)[0x3fff7e8d5bd4] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontendTool.so.10svn(_ZN5clang25ExecuteCompilerInvocationEPNS_16CompilerInstanceE+0x830)[0x3fff7e8042f0] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang(_Z8cc1_mainN4llvm8ArrayRefIPKcEES2_Pv+0x674)[0x10012594] >>> >>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang(main+0x321c)[0x1000f37c] >>> /lib64/libc.so.6(+0x25100)[0x3fff7e085100] >>> /lib64/libc.so.6(__libc_start_main+0xc4)[0x3fff7e0852f4] >>> >>> >>> On Tue, Oct 22, 2019 at 9:18 PM Richard Smith via cfe-commits < >>> cfe-commits@lists.llvm.org> wrote: >>> >>>> >>>> Author: Richard Smith >>>> Date: 2019-10-22T18:16:17-07:00 >>>> New Revision: d052a578de58cbbb638cbe2dba05242d1ff443b9 >>>> >>>> URL: >>>> https://github.com/llvm/llvm-project/commit/d052a578de58cbbb638cbe2dba05242d1ff443b9 >>>> DIFF: >>>> https://github.com/llvm/llvm-project/commit/d052a578de58cbbb638cbe2dba05242d1ff443b9.diff >>>> >>>> LOG: [c++2a] Allow comparison functions to be explicitly defaulted. >>>> >>>> This adds some initial syntactic checking that only the appropriate >>>> function signatures can be defaulted. No implicit definitions are >>>> generated yet. >>>> >>>> Added: >>>> clang/test/CXX/class/class.compare/class.compare.default/p1.cpp >>>> clang/test/CXX/class/class.compare/class.eq/p1.cpp >>>> clang/test/CXX/class/class.compare/class.rel/p1.cpp >>>> >>>> Modified: >>>> clang/include/clang/AST/Decl.h >>>> clang/include/clang/Basic/DiagnosticCommonKinds.td >>>> clang/include/clang/Basic/DiagnosticSemaKinds.td >>>> clang/include/clang/Sema/Sema.h >>>> clang/lib/AST/Decl.cpp >>>> clang/lib/Parse/ParseDecl.cpp >>>> clang/lib/Parse/ParseDeclCXX.cpp >>>> clang/lib/Sema/SemaDecl.cpp >>>> clang/lib/Sema/SemaDeclCXX.cpp >>>> clang/lib/Sema/SemaTemplateInstantiateDecl.cpp >>>> clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p1.cpp >>>> clang/test/Parser/cxx0x-decl.cpp >>>> clang/test/SemaCXX/cxx0x-defaulted-functions.cpp >>>> clang/test/SemaCXX/cxx17-compat.cpp >>>> >>>> Removed: >>>> >>>> >>>> >>>> >>>> ################################################################################ >>>> diff --git a/clang/include/clang/AST/Decl.h >>>> b/clang/include/clang/AST/Decl.h >>>> index ce674e09c44d..b3e7a570fd6d 100644 >>>> --- a/clang/include/clang/AST/Decl.h >>>> +++ b/clang/include/clang/AST/Decl.h >>>> @@ -59,6 +59,7 @@ class EnumDecl; >>>> class Expr; >>>> class FunctionTemplateDecl; >>>> class FunctionTemplateSpecializationInfo; >>>> +class FunctionTypeLoc; >>>> class LabelStmt; >>>> class MemberSpecializationInfo; >>>> class Module; >>>> @@ -2362,6 +2363,12 @@ class FunctionDecl : public DeclaratorDecl, >>>> /// parameters have default arguments (in C++). >>>> unsigned getMinRequiredArguments() const; >>>> >>>> + /// Find the source location information for how the type of this >>>> function >>>> + /// was written. May be absent (for example if the function was >>>> declared via >>>> + /// a typedef) and may contain a >>>> diff erent type from that of the function >>>> + /// (for example if the function type was adjusted by an attribute). >>>> + FunctionTypeLoc getFunctionTypeLoc() const; >>>> + >>>> QualType getReturnType() const { >>>> return getType()->castAs<FunctionType>()->getReturnType(); >>>> } >>>> >>>> diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td >>>> b/clang/include/clang/Basic/DiagnosticCommonKinds.td >>>> index 6018c1417789..7a416c282e3d 100644 >>>> --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td >>>> +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td >>>> @@ -87,7 +87,8 @@ def warn_cxx98_compat_variadic_templates : >>>> Warning<"variadic templates are incompatible with C++98">, >>>> InGroup<CXX98Compat>, DefaultIgnore; >>>> def err_default_special_members : Error< >>>> - "only special member functions may be defaulted">; >>>> + "only special member functions %select{|and comparison operators }0" >>>> + "may be defaulted">; >>>> def err_deleted_non_function : Error< >>>> "only functions can have deleted definitions">; >>>> def err_module_not_found : Error<"module '%0' not found">, >>>> DefaultFatal; >>>> >>>> diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td >>>> b/clang/include/clang/Basic/DiagnosticSemaKinds.td >>>> index d802a92c42c0..f7b98bb9ea86 100644 >>>> --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td >>>> +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td >>>> @@ -8099,6 +8099,31 @@ def note_vbase_moved_here : Note< >>>> "%select{%1 is a virtual base class of base class %2 declared here|" >>>> "virtual base class %1 declared here}0">; >>>> >>>> +// C++20 defaulted comparisons >>>> +// This corresponds to values of Sema::DefaultedComparisonKind. >>>> +def select_defaulted_comparison_kind : TextSubstitution< >>>> + "%select{<ERROR>|equality|three-way|equality|relational}0 comparison >>>> " >>>> + "operator">; >>>> +def ext_defaulted_comparison : ExtWarn< >>>> + "defaulted comparison operators are a C++20 extension">, >>>> InGroup<CXX2a>; >>>> +def warn_cxx17_compat_defaulted_comparison : Warning< >>>> + "defaulted comparison operators are incompatible with C++ standards " >>>> + "before C++20">, InGroup<CXXPre2aCompat>, DefaultIgnore; >>>> +def err_defaulted_comparison_template : Error< >>>> + "comparison operator template cannot be defaulted">; >>>> +def err_defaulted_comparison_out_of_class : Error< >>>> + "%sub{select_defaulted_comparison_kind}0 can only be defaulted in a >>>> class " >>>> + "definition">; >>>> +def err_defaulted_comparison_param : Error< >>>> + "invalid parameter type for defaulted >>>> %sub{select_defaulted_comparison_kind}0" >>>> + "% >>>> diff {; found $, expected $|}1,2">; >>>> +def err_defaulted_comparison_non_const : Error< >>>> + "defaulted member %sub{select_defaulted_comparison_kind}0 must be " >>>> + "const-qualified">; >>>> +def err_defaulted_comparison_return_type_not_bool : Error< >>>> + "return type for defaulted %sub{select_defaulted_comparison_kind}0 " >>>> + "must be 'bool', not %1">; >>>> + >>>> def ext_implicit_exception_spec_mismatch : ExtWarn< >>>> "function previously declared with an %select{explicit|implicit}0 >>>> exception " >>>> "specification redeclared with an %select{implicit|explicit}0 >>>> exception " >>>> >>>> diff --git a/clang/include/clang/Sema/Sema.h >>>> b/clang/include/clang/Sema/Sema.h >>>> index a911c61a07f8..3058f862c6ec 100644 >>>> --- a/clang/include/clang/Sema/Sema.h >>>> +++ b/clang/include/clang/Sema/Sema.h >>>> @@ -1237,6 +1237,24 @@ class Sema { >>>> /// same special member, we should act as if it is not yet declared. >>>> llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared; >>>> >>>> + /// Kinds of defaulted comparison operator functions. >>>> + enum class DefaultedComparisonKind { >>>> + /// This is not a defaultable comparison operator. >>>> + None, >>>> + /// This is an operator== that should be implemented as a series of >>>> + /// subobject comparisons. >>>> + Equal, >>>> + /// This is an operator<=> that should be implemented as a series >>>> of >>>> + /// subobject comparisons. >>>> + ThreeWay, >>>> + /// This is an operator!= that should be implemented as a rewrite >>>> in terms >>>> + /// of a == comparison. >>>> + NotEqual, >>>> + /// This is an <, <=, >, or >= that should be implemented as a >>>> rewrite in >>>> + /// terms of a <=> comparison. >>>> + Relational, >>>> + }; >>>> + >>>> /// The function definitions which were renamed as part of >>>> typo-correction >>>> /// to match their respective declarations. We want to keep track of >>>> them >>>> /// to ensure that we don't emit a "redefinition" error if we >>>> encounter a >>>> @@ -2541,7 +2559,52 @@ class Sema { >>>> bool SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM, >>>> TrivialABIHandling TAH = >>>> TAH_IgnoreTrivialABI, >>>> bool Diagnose = false); >>>> - CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD); >>>> + >>>> + /// For a defaulted function, the kind of defaulted function that it >>>> is. >>>> + class DefaultedFunctionKind { >>>> + CXXSpecialMember SpecialMember : 8; >>>> + DefaultedComparisonKind Comparison : 8; >>>> + >>>> + public: >>>> + DefaultedFunctionKind() >>>> + : SpecialMember(CXXInvalid), >>>> Comparison(DefaultedComparisonKind::None) { >>>> + } >>>> + DefaultedFunctionKind(CXXSpecialMember CSM) >>>> + : SpecialMember(CSM), >>>> Comparison(DefaultedComparisonKind::None) {} >>>> + DefaultedFunctionKind(DefaultedComparisonKind Comp) >>>> + : SpecialMember(CXXInvalid), Comparison(Comp) {} >>>> + >>>> + bool isSpecialMember() const { return SpecialMember != CXXInvalid; >>>> } >>>> + bool isComparison() const { >>>> + return Comparison != DefaultedComparisonKind::None; >>>> + } >>>> + >>>> + explicit operator bool() const { >>>> + return isSpecialMember() || isComparison(); >>>> + } >>>> + >>>> + CXXSpecialMember asSpecialMember() const { return SpecialMember; } >>>> + DefaultedComparisonKind asComparison() const { return Comparison; } >>>> + >>>> + /// Get the index of this function kind for use in diagnostics. >>>> + unsigned getDiagnosticIndex() const { >>>> + static_assert(CXXInvalid > CXXDestructor, >>>> + "invalid should have highest index"); >>>> + static_assert((unsigned)DefaultedComparisonKind::None == 0, >>>> + "none should be equal to zero"); >>>> + return SpecialMember + (unsigned)Comparison; >>>> + } >>>> + }; >>>> + >>>> + DefaultedFunctionKind getDefaultedFunctionKind(const FunctionDecl >>>> *FD); >>>> + >>>> + CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD) { >>>> + return getDefaultedFunctionKind(MD).asSpecialMember(); >>>> + } >>>> + DefaultedComparisonKind getDefaultedComparisonKind(const >>>> FunctionDecl *FD) { >>>> + return getDefaultedFunctionKind(FD).asComparison(); >>>> + } >>>> + >>>> void ActOnLastBitfield(SourceLocation DeclStart, >>>> SmallVectorImpl<Decl *> &AllIvarDecls); >>>> Decl *ActOnIvar(Scope *S, SourceLocation DeclStart, >>>> @@ -6361,9 +6424,15 @@ class Sema { >>>> StorageClass &SC); >>>> void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD); >>>> >>>> - void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD); >>>> + void CheckExplicitlyDefaultedFunction(FunctionDecl *MD); >>>> + >>>> + bool CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD, >>>> + CXXSpecialMember CSM); >>>> void CheckDelayedMemberExceptionSpecs(); >>>> >>>> + bool CheckExplicitlyDefaultedComparison(FunctionDecl *MD, >>>> + DefaultedComparisonKind DCK); >>>> + >>>> >>>> >>>> //===--------------------------------------------------------------------===// >>>> // C++ Derived Classes >>>> // >>>> >>>> diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp >>>> index 80235d8496d2..dae4af8bb249 100644 >>>> --- a/clang/lib/AST/Decl.cpp >>>> +++ b/clang/lib/AST/Decl.cpp >>>> @@ -3322,12 +3322,14 @@ bool >>>> FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { >>>> return FoundBody; >>>> } >>>> >>>> -SourceRange FunctionDecl::getReturnTypeSourceRange() const { >>>> +FunctionTypeLoc FunctionDecl::getFunctionTypeLoc() const { >>>> const TypeSourceInfo *TSI = getTypeSourceInfo(); >>>> - if (!TSI) >>>> - return SourceRange(); >>>> - FunctionTypeLoc FTL = >>>> - TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>(); >>>> + return TSI ? >>>> TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>() >>>> + : FunctionTypeLoc(); >>>> +} >>>> + >>>> +SourceRange FunctionDecl::getReturnTypeSourceRange() const { >>>> + FunctionTypeLoc FTL = getFunctionTypeLoc(); >>>> if (!FTL) >>>> return SourceRange(); >>>> >>>> @@ -3343,15 +3345,8 @@ SourceRange >>>> FunctionDecl::getReturnTypeSourceRange() const { >>>> } >>>> >>>> SourceRange FunctionDecl::getExceptionSpecSourceRange() const { >>>> - const TypeSourceInfo *TSI = getTypeSourceInfo(); >>>> - if (!TSI) >>>> - return SourceRange(); >>>> - FunctionTypeLoc FTL = >>>> - TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>(); >>>> - if (!FTL) >>>> - return SourceRange(); >>>> - >>>> - return FTL.getExceptionSpecRange(); >>>> + FunctionTypeLoc FTL = getFunctionTypeLoc(); >>>> + return FTL ? FTL.getExceptionSpecRange() : SourceRange(); >>>> } >>>> >>>> /// For an inline function definition in C, or for a gnu_inline >>>> function >>>> >>>> diff --git a/clang/lib/Parse/ParseDecl.cpp >>>> b/clang/lib/Parse/ParseDecl.cpp >>>> index b248d7582d84..c41eb74a9cf3 100644 >>>> --- a/clang/lib/Parse/ParseDecl.cpp >>>> +++ b/clang/lib/Parse/ParseDecl.cpp >>>> @@ -2349,7 +2349,8 @@ Decl >>>> *Parser::ParseDeclarationAfterDeclaratorAndAttributes( >>>> Diag(ConsumeToken(), >>>> diag::err_default_delete_in_multiple_declaration) >>>> << 0 /* default */; >>>> else >>>> - Diag(ConsumeToken(), diag::err_default_special_members); >>>> + Diag(ConsumeToken(), diag::err_default_special_members) >>>> + << getLangOpts().CPlusPlus2a; >>>> } else { >>>> InitializerScopeRAII InitScope(*this, D, ThisDecl); >>>> >>>> >>>> diff --git a/clang/lib/Parse/ParseDeclCXX.cpp >>>> b/clang/lib/Parse/ParseDeclCXX.cpp >>>> index b98ce3e66292..6d4a1a4a4e87 100644 >>>> --- a/clang/lib/Parse/ParseDeclCXX.cpp >>>> +++ b/clang/lib/Parse/ParseDeclCXX.cpp >>>> @@ -2978,7 +2978,8 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl >>>> *D, bool IsFunction, >>>> Diag(Tok, diag::err_default_delete_in_multiple_declaration) >>>> << 0 /* default */; >>>> else >>>> - Diag(ConsumeToken(), diag::err_default_special_members); >>>> + Diag(ConsumeToken(), diag::err_default_special_members) >>>> + << getLangOpts().CPlusPlus2a; >>>> return ExprError(); >>>> } >>>> } >>>> >>>> diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp >>>> index 62ec83967bff..6202391ee0b8 100644 >>>> --- a/clang/lib/Sema/SemaDecl.cpp >>>> +++ b/clang/lib/Sema/SemaDecl.cpp >>>> @@ -2993,28 +2993,6 @@ struct GNUCompatibleParamWarning { >>>> >>>> } // end anonymous namespace >>>> >>>> -/// getSpecialMember - get the special member enum for a method. >>>> -Sema::CXXSpecialMember Sema::getSpecialMember(const CXXMethodDecl *MD) >>>> { >>>> - if (const CXXConstructorDecl *Ctor = >>>> dyn_cast<CXXConstructorDecl>(MD)) { >>>> - if (Ctor->isDefaultConstructor()) >>>> - return Sema::CXXDefaultConstructor; >>>> - >>>> - if (Ctor->isCopyConstructor()) >>>> - return Sema::CXXCopyConstructor; >>>> - >>>> - if (Ctor->isMoveConstructor()) >>>> - return Sema::CXXMoveConstructor; >>>> - } else if (isa<CXXDestructorDecl>(MD)) { >>>> - return Sema::CXXDestructor; >>>> - } else if (MD->isCopyAssignmentOperator()) { >>>> - return Sema::CXXCopyAssignment; >>>> - } else if (MD->isMoveAssignmentOperator()) { >>>> - return Sema::CXXMoveAssignment; >>>> - } >>>> - >>>> - return Sema::CXXInvalid; >>>> -} >>>> - >>>> // Determine whether the previous declaration was a definition, >>>> implicit >>>> // declaration, or a declaration. >>>> template <typename T> >>>> >>>> diff --git a/clang/lib/Sema/SemaDeclCXX.cpp >>>> b/clang/lib/Sema/SemaDeclCXX.cpp >>>> index ff90b9548e29..0201d014e6f2 100644 >>>> --- a/clang/lib/Sema/SemaDeclCXX.cpp >>>> +++ b/clang/lib/Sema/SemaDeclCXX.cpp >>>> @@ -6084,6 +6084,67 @@ void Sema::propagateDLLAttrToBaseClassTemplate( >>>> } >>>> } >>>> >>>> +/// Determine the kind of defaulting that would be done for a given >>>> function. >>>> +/// >>>> +/// If the function is both a default constructor and a copy / move >>>> constructor >>>> +/// (due to having a default argument for the first parameter), this >>>> picks >>>> +/// CXXDefaultConstructor. >>>> +/// >>>> +/// FIXME: Check that case is properly handled by all callers. >>>> +Sema::DefaultedFunctionKind >>>> +Sema::getDefaultedFunctionKind(const FunctionDecl *FD) { >>>> + if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { >>>> + if (const CXXConstructorDecl *Ctor = >>>> dyn_cast<CXXConstructorDecl>(FD)) { >>>> + if (Ctor->isDefaultConstructor()) >>>> + return Sema::CXXDefaultConstructor; >>>> + >>>> + if (Ctor->isCopyConstructor()) >>>> + return Sema::CXXCopyConstructor; >>>> + >>>> + if (Ctor->isMoveConstructor()) >>>> + return Sema::CXXMoveConstructor; >>>> + } >>>> + >>>> + if (MD->isCopyAssignmentOperator()) >>>> + return Sema::CXXCopyAssignment; >>>> + >>>> + if (MD->isMoveAssignmentOperator()) >>>> + return Sema::CXXMoveAssignment; >>>> + >>>> + if (isa<CXXDestructorDecl>(FD)) >>>> + return Sema::CXXDestructor; >>>> + } >>>> + >>>> + switch (FD->getDeclName().getCXXOverloadedOperator()) { >>>> + case OO_EqualEqual: >>>> + return DefaultedComparisonKind::Equal; >>>> + >>>> + case OO_ExclaimEqual: >>>> + return DefaultedComparisonKind::NotEqual; >>>> + >>>> + case OO_Spaceship: >>>> + // No point allowing this if <=> doesn't exist in the current >>>> language mode. >>>> + if (!getLangOpts().CPlusPlus2a) >>>> + break; >>>> + return DefaultedComparisonKind::ThreeWay; >>>> + >>>> + case OO_Less: >>>> + case OO_LessEqual: >>>> + case OO_Greater: >>>> + case OO_GreaterEqual: >>>> + // No point allowing this if <=> doesn't exist in the current >>>> language mode. >>>> + if (!getLangOpts().CPlusPlus2a) >>>> + break; >>>> + return DefaultedComparisonKind::Relational; >>>> + >>>> + default: >>>> + break; >>>> + } >>>> + >>>> + // Not defaultable. >>>> + return DefaultedFunctionKind(); >>>> +} >>>> + >>>> static void DefineImplicitSpecialMember(Sema &S, CXXMethodDecl *MD, >>>> SourceLocation DefaultLoc) { >>>> switch (S.getSpecialMember(MD)) { >>>> @@ -6331,9 +6392,9 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl >>>> *Record) { >>>> Record->setHasTrivialSpecialMemberForCall(); >>>> >>>> auto CompleteMemberFunction = [&](CXXMethodDecl *M) { >>>> - // Check whether the explicitly-defaulted special members are >>>> valid. >>>> + // Check whether the explicitly-defaulted members are valid. >>>> if (!M->isInvalidDecl() && M->isExplicitlyDefaulted()) >>>> - CheckExplicitlyDefaultedSpecialMember(M); >>>> + CheckExplicitlyDefaultedFunction(M); >>>> >>>> // For an explicitly defaulted or deleted special member, we defer >>>> // determining triviality until the class is complete. That time >>>> is now! >>>> @@ -6413,6 +6474,15 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl >>>> *Record) { >>>> DiagnoseAbsenceOfOverrideControl(M); >>>> } >>>> >>>> + // Process any defaulted friends in the member-specification. >>>> + if (!Record->isDependentType()) { >>>> + for (FriendDecl *D : Record->friends()) { >>>> + auto *FD = dyn_cast_or_null<FunctionDecl>(D->getFriendDecl()); >>>> + if (FD && !FD->isInvalidDecl() && FD->isExplicitlyDefaulted()) >>>> + CheckExplicitlyDefaultedFunction(FD); >>>> + } >>>> + } >>>> + >>>> // ms_struct is a request to use the same ABI rules as MSVC. Check >>>> // whether this class uses any C++ features that are implemented >>>> // completely >>>> diff erently in MSVC, and if so, emit a diagnostic. >>>> @@ -6766,9 +6836,22 @@ void >>>> Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD) >>>> UpdateExceptionSpec(MD->getCanonicalDecl(), ESI); >>>> } >>>> >>>> -void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { >>>> +void Sema::CheckExplicitlyDefaultedFunction(FunctionDecl *FD) { >>>> + assert(FD->isExplicitlyDefaulted() && "not explicitly-defaulted"); >>>> + >>>> + DefaultedFunctionKind DefKind = getDefaultedFunctionKind(FD); >>>> + assert(DefKind && "not a defaultable function"); >>>> + >>>> + if (DefKind.isSpecialMember() >>>> + ? >>>> CheckExplicitlyDefaultedSpecialMember(cast<CXXMethodDecl>(FD), >>>> + >>>> DefKind.asSpecialMember()) >>>> + : CheckExplicitlyDefaultedComparison(FD, >>>> DefKind.asComparison())) >>>> + FD->setInvalidDecl(); >>>> +} >>>> + >>>> +bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD, >>>> + CXXSpecialMember CSM) >>>> { >>>> CXXRecordDecl *RD = MD->getParent(); >>>> - CXXSpecialMember CSM = getSpecialMember(MD); >>>> >>>> assert(MD->isExplicitlyDefaulted() && CSM != CXXInvalid && >>>> "not an explicitly-defaulted special member"); >>>> @@ -6781,7 +6864,7 @@ void >>>> Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { >>>> >>>> // C++11 [dcl.fct.def.default]p1: >>>> // A function that is explicitly defaulted shall >>>> - // -- be a special member function (checked elsewhere), >>>> + // -- be a special member function [...] (checked elsewhere), >>>> // -- have the same type (except for ref-qualifiers, and except >>>> that a >>>> // copy operation can take a non-const reference) as an >>>> implicit >>>> // declaration, and >>>> @@ -6960,8 +7043,87 @@ void >>>> Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { >>>> } >>>> } >>>> >>>> - if (HadError) >>>> - MD->setInvalidDecl(); >>>> + return HadError; >>>> +} >>>> + >>>> +bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD, >>>> + DefaultedComparisonKind >>>> DCK) { >>>> + assert(DCK != DefaultedComparisonKind::None && "not a defaulted >>>> comparison"); >>>> + >>>> + // C++2a [class.compare.default]p1: >>>> + // A defaulted comparison operator function for some class C shall >>>> be a >>>> + // non-template function declared in the member-specification of C >>>> that is >>>> + // -- a non-static const member of C having one parameter of type >>>> + // const C&, or >>>> + // -- a friend of C having two parameters of type const C&. >>>> + CXXRecordDecl *RD = >>>> dyn_cast<CXXRecordDecl>(FD->getLexicalDeclContext()); >>>> + assert(RD && "defaulted comparison is not defaulted in a class"); >>>> + >>>> + QualType ExpectedParmType = >>>> + >>>> Context.getLValueReferenceType(Context.getRecordType(RD).withConst()); >>>> + for (const ParmVarDecl *Param : FD->parameters()) { >>>> + if (!Context.hasSameType(Param->getType(), ExpectedParmType)) { >>>> + Diag(FD->getLocation(), diag::err_defaulted_comparison_param) >>>> + << (int)DCK << Param->getType() << ExpectedParmType >>>> + << Param->getSourceRange(); >>>> + return true; >>>> + } >>>> + } >>>> + >>>> + // ... non-static const member ... >>>> + if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { >>>> + assert(!MD->isStatic() && "comparison function cannot be a static >>>> member"); >>>> + if (!MD->isConst()) { >>>> + SourceLocation InsertLoc; >>>> + if (FunctionTypeLoc Loc = MD->getFunctionTypeLoc()) >>>> + InsertLoc = getLocForEndOfToken(Loc.getRParenLoc()); >>>> + Diag(MD->getLocation(), diag::err_defaulted_comparison_non_const) >>>> + << (int)DCK << FixItHint::CreateInsertion(InsertLoc, " const"); >>>> + >>>> + // Add the 'const' to the type to recover. >>>> + const auto *FPT = MD->getType()->castAs<FunctionProtoType>(); >>>> + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); >>>> + EPI.TypeQuals.addConst(); >>>> + MD->setType(Context.getFunctionType(FPT->getReturnType(), >>>> + FPT->getParamTypes(), EPI)); >>>> + } >>>> + } else { >>>> + // A non-member function declared in a class must be a friend. >>>> + assert(FD->getFriendObjectKind() && "expected a friend >>>> declaration"); >>>> + } >>>> + >>>> + // C++2a [class.compare.default]p2: >>>> + // A defaulted comparison operator function for class C is defined >>>> as >>>> + // deleted if any non-static data member of C is of reference type >>>> or C is >>>> + // a union-like class. >>>> + // FIXME: Applying this to cases other than == and <=> is >>>> unreasonable. >>>> + // FIXME: Implement. >>>> + >>>> + // C++2a [class.eq]p1, [class.rel]p1: >>>> + // A [defaulted comparison other than <=>] shall have a declared >>>> return >>>> + // type bool. >>>> + if (DCK != DefaultedComparisonKind::ThreeWay && >>>> + !Context.hasSameType(FD->getDeclaredReturnType(), >>>> Context.BoolTy)) { >>>> + Diag(FD->getLocation(), >>>> diag::err_defaulted_comparison_return_type_not_bool) >>>> + << (int)DCK << FD->getDeclaredReturnType() << Context.BoolTy >>>> + << FD->getReturnTypeSourceRange(); >>>> + return true; >>>> + } >>>> + >>>> + // FIXME: Determine whether the function should be defined as >>>> deleted. >>>> + >>>> + // C++2a [dcl.fct.def.default]p3: >>>> + // An explicitly-defaulted function [..] may be declared constexpr >>>> or >>>> + // consteval only if it would have been implicitly declared >>>> constexpr. >>>> + // FIXME: There are no rules governing when these should be >>>> constexpr, >>>> + // except for the special case of the injected operator==, for which >>>> + // C++2a [class.compare.default]p3 says: >>>> + // The operator is a constexpr function if its definition would >>>> satisfy >>>> + // the requirements for a constexpr function. >>>> + // FIXME: Apply this rule to all defaulted comparisons. The only way >>>> this >>>> + // can fail is if the return type of a defaulted operator<=> is not >>>> a literal >>>> + // type. >>>> + return false; >>>> } >>>> >>>> void Sema::CheckDelayedMemberExceptionSpecs() { >>>> @@ -15006,51 +15168,88 @@ void Sema::SetDeclDeleted(Decl *Dcl, >>>> SourceLocation DelLoc) { >>>> } >>>> >>>> void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { >>>> - CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Dcl); >>>> + if (!Dcl || Dcl->isInvalidDecl()) >>>> + return; >>>> >>>> - if (MD) { >>>> - if (MD->getParent()->isDependentType()) { >>>> - MD->setDefaulted(); >>>> - MD->setExplicitlyDefaulted(); >>>> - return; >>>> + auto *FD = dyn_cast<FunctionDecl>(Dcl); >>>> + if (!FD) { >>>> + if (auto *FTD = dyn_cast<FunctionTemplateDecl>(Dcl)) { >>>> + if >>>> (getDefaultedFunctionKind(FTD->getTemplatedDecl()).isComparison()) { >>>> + Diag(DefaultLoc, diag::err_defaulted_comparison_template); >>>> + return; >>>> + } >>>> } >>>> >>>> - CXXSpecialMember Member = getSpecialMember(MD); >>>> - if (Member == CXXInvalid) { >>>> - if (!MD->isInvalidDecl()) >>>> - Diag(DefaultLoc, diag::err_default_special_members); >>>> - return; >>>> - } >>>> + Diag(DefaultLoc, diag::err_default_special_members) >>>> + << getLangOpts().CPlusPlus2a; >>>> + return; >>>> + } >>>> >>>> - MD->setDefaulted(); >>>> - MD->setExplicitlyDefaulted(); >>>> + // Reject if this can't possibly be a defaultable function. >>>> + DefaultedFunctionKind DefKind = getDefaultedFunctionKind(FD); >>>> + if (!DefKind && >>>> + // A dependent function that doesn't locally look defaultable can >>>> + // still instantiate to a defaultable function if it's a >>>> constructor >>>> + // or assignment operator. >>>> + (!FD->isDependentContext() || >>>> + (!isa<CXXConstructorDecl>(FD) && >>>> + FD->getDeclName().getCXXOverloadedOperator() != OO_Equal))) { >>>> + Diag(DefaultLoc, diag::err_default_special_members) >>>> + << getLangOpts().CPlusPlus2a; >>>> + return; >>>> + } >>>> >>>> - // Unset that we will have a body for this function. We might not, >>>> - // if it turns out to be trivial, and we don't need this marking >>>> now >>>> - // that we've marked it as defaulted. >>>> - MD->setWillHaveBody(false); >>>> + if (DefKind.isComparison() && >>>> + !isa<CXXRecordDecl>(FD->getLexicalDeclContext())) { >>>> + Diag(FD->getLocation(), >>>> diag::err_defaulted_comparison_out_of_class) >>>> + << (int)DefKind.asComparison(); >>>> + return; >>>> + } >>>> >>>> - // If this definition appears within the record, do the checking >>>> when >>>> - // the record is complete. >>>> - const FunctionDecl *Primary = MD; >>>> - if (const FunctionDecl *Pattern = >>>> MD->getTemplateInstantiationPattern()) >>>> - // Ask the template instantiation pattern that actually had the >>>> - // '= default' on it. >>>> - Primary = Pattern; >>>> + // Issue compatibility warning. We already warned if the operator is >>>> + // 'operator<=>' when parsing the '<=>' token. >>>> + if (DefKind.isComparison() && >>>> + DefKind.asComparison() != DefaultedComparisonKind::ThreeWay) { >>>> + Diag(DefaultLoc, getLangOpts().CPlusPlus2a >>>> + ? diag::warn_cxx17_compat_defaulted_comparison >>>> + : diag::ext_defaulted_comparison); >>>> + } >>>> >>>> - // If the method was defaulted on its first declaration, we will >>>> have >>>> - // already performed the checking in CheckCompletedCXXClass. Such a >>>> - // declaration doesn't trigger an implicit definition. >>>> - if (Primary->getCanonicalDecl()->isDefaulted()) >>>> - return; >>>> + FD->setDefaulted(); >>>> + FD->setExplicitlyDefaulted(); >>>> >>>> - CheckExplicitlyDefaultedSpecialMember(MD); >>>> + // Defer checking functions that are defaulted in a dependent >>>> context. >>>> + if (FD->isDependentContext()) >>>> + return; >>>> >>>> - if (!MD->isInvalidDecl()) >>>> - DefineImplicitSpecialMember(*this, MD, DefaultLoc); >>>> - } else { >>>> - Diag(DefaultLoc, diag::err_default_special_members); >>>> - } >>>> + // Unset that we will have a body for this function. We might not, >>>> + // if it turns out to be trivial, and we don't need this marking now >>>> + // that we've marked it as defaulted. >>>> + FD->setWillHaveBody(false); >>>> + >>>> + // If this definition appears within the record, do the checking when >>>> + // the record is complete. This is always the case for a defaulted >>>> + // comparison. >>>> + if (DefKind.isComparison()) >>>> + return; >>>> + auto *MD = cast<CXXMethodDecl>(FD); >>>> + >>>> + const FunctionDecl *Primary = FD; >>>> + if (const FunctionDecl *Pattern = >>>> FD->getTemplateInstantiationPattern()) >>>> + // Ask the template instantiation pattern that actually had the >>>> + // '= default' on it. >>>> + Primary = Pattern; >>>> + >>>> + // If the method was defaulted on its first declaration, we will have >>>> + // already performed the checking in CheckCompletedCXXClass. Such a >>>> + // declaration doesn't trigger an implicit definition. >>>> + if (Primary->getCanonicalDecl()->isDefaulted()) >>>> + return; >>>> + >>>> + if (CheckExplicitlyDefaultedSpecialMember(MD, >>>> DefKind.asSpecialMember())) >>>> + MD->setInvalidDecl(); >>>> + else >>>> + DefineImplicitSpecialMember(*this, MD, DefaultLoc); >>>> } >>>> >>>> static void SearchForReturnInStmt(Sema &Self, Stmt *S) { >>>> >>>> diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp >>>> b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp >>>> index d1ad304e62e4..31a4302ba826 100644 >>>> --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp >>>> +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp >>>> @@ -2049,6 +2049,11 @@ Decl >>>> *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, >>>> } >>>> } >>>> >>>> + if (D->isExplicitlyDefaulted()) >>>> + SemaRef.SetDeclDefaulted(Function, D->getLocation()); >>>> + if (D->isDeleted()) >>>> + SemaRef.SetDeclDeleted(Function, D->getLocation()); >>>> + >>>> if (Function->isLocalExternDecl() && !Function->getPreviousDecl()) >>>> DC->makeDeclVisibleInContext(PrincipalDecl); >>>> >>>> @@ -2056,7 +2061,6 @@ Decl >>>> *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, >>>> PrincipalDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary)) >>>> PrincipalDecl->setNonMemberOperator(); >>>> >>>> - assert(!D->isDefaulted() && "only methods should be defaulted"); >>>> return Function; >>>> } >>>> >>>> @@ -4016,9 +4020,6 @@ void >>>> Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation, >>>> bool >>>> TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, >>>> FunctionDecl >>>> *Tmpl) { >>>> - if (Tmpl->isDeleted()) >>>> - New->setDeletedAsWritten(); >>>> - >>>> New->setImplicit(Tmpl->isImplicit()); >>>> >>>> // Forward the mangling number from the template to the instantiated >>>> decl. >>>> >>>> diff --git >>>> a/clang/test/CXX/class/class.compare/class.compare.default/p1.cpp >>>> b/clang/test/CXX/class/class.compare/class.compare.default/p1.cpp >>>> new file mode 100644 >>>> index 000000000000..1f8d6a2a7cff >>>> --- /dev/null >>>> +++ b/clang/test/CXX/class/class.compare/class.compare.default/p1.cpp >>>> @@ -0,0 +1,46 @@ >>>> +// RUN: %clang_cc1 -std=c++2a -verify %s >>>> + >>>> +struct B {}; >>>> +bool operator==(const B&, const B&) = default; // expected-error >>>> {{equality comparison operator can only be defaulted in a class >>>> definition}} >>>> +bool operator<=>(const B&, const B&) = default; // expected-error >>>> {{three-way comparison operator can only be defaulted in a class >>>> definition}} >>>> + >>>> +template<typename T = void> >>>> + bool operator<(const B&, const B&) = default; // expected-error >>>> {{comparison operator template cannot be defaulted}} >>>> + >>>> +struct A { >>>> + friend bool operator==(const A&, const A&) = default; >>>> + friend bool operator!=(const A&, const B&) = default; // >>>> expected-error {{invalid parameter type for defaulted equality comparison}} >>>> + friend bool operator!=(const B&, const B&) = default; // >>>> expected-error {{invalid parameter type for defaulted equality comparison}} >>>> + friend bool operator<(const A&, const A&); >>>> + friend bool operator<(const B&, const B&) = default; // >>>> expected-error {{invalid parameter type for defaulted relational >>>> comparison}} >>>> + friend bool operator>(A, A) = default; // expected-error {{invalid >>>> parameter type for defaulted relational comparison}} >>>> + >>>> + bool operator<(const A&) const; >>>> + bool operator<=(const A&) const = default; >>>> + bool operator==(const A&) const volatile && = default; // >>>> surprisingly, OK >>>> + bool operator<=>(const A&) = default; // expected-error {{defaulted >>>> member three-way comparison operator must be const-qualified}} >>>> + bool operator>=(const B&) const = default; // expected-error >>>> {{invalid parameter type for defaulted relational comparison}} >>>> + static bool operator>(const B&) = default; // expected-error >>>> {{overloaded 'operator>' cannot be a static member function}} >>>> + >>>> + template<typename T = void> >>>> + friend bool operator==(const A&, const A&) = default; // >>>> expected-error {{comparison operator template cannot be defaulted}} >>>> + template<typename T = void> >>>> + bool operator==(const A&) const = default; // expected-error >>>> {{comparison operator template cannot be defaulted}} >>>> +}; >>>> + >>>> +// FIXME: The wording is not clear as to whether these are valid, but >>>> the >>>> +// intention is that they are not. >>>> +bool operator<(const A&, const A&) = default; // expected-error >>>> {{relational comparison operator can only be defaulted in a class >>>> definition}} >>>> +bool A::operator<(const A&) const = default; // expected-error {{can >>>> only be defaulted in a class definition}} >>>> + >>>> +template<typename T> struct Dependent { >>>> + using U = typename T::type; >>>> + bool operator==(U) const = default; // expected-error {{found >>>> 'Dependent<Bad>::U'}} >>>> + friend bool operator==(U, U) = default; // expected-error {{found >>>> 'Dependent<Bad>::U'}} >>>> +}; >>>> + >>>> +struct Good { using type = const Dependent<Good>&; }; >>>> +template struct Dependent<Good>; >>>> + >>>> +struct Bad { using type = Dependent<Bad>&; }; >>>> +template struct Dependent<Bad>; // expected-note {{in instantiation >>>> of}} >>>> >>>> diff --git a/clang/test/CXX/class/class.compare/class.eq/p1.cpp >>>> b/clang/test/CXX/class/class.compare/class.eq/p1.cpp >>>> new file mode 100644 >>>> index 000000000000..622f66cf9281 >>>> --- /dev/null >>>> +++ b/clang/test/CXX/class/class.compare/class.eq/p1.cpp >>>> @@ -0,0 +1,25 @@ >>>> +// RUN: %clang_cc1 -std=c++2a -verify %s >>>> + >>>> +struct Good { >>>> + bool operator==(const Good&) const = default; >>>> + bool operator!=(const Good&) const = default; >>>> + friend bool operator==(const Good&, const Good&) = default; >>>> + friend bool operator!=(const Good&, const Good&) = default; >>>> +}; >>>> + >>>> +enum Bool : bool {}; >>>> +struct Bad { >>>> + bool &operator==(const Bad&) const = default; // expected-error >>>> {{return type for defaulted equality comparison operator must be 'bool', >>>> not 'bool &'}} >>>> + const bool operator!=(const Bad&) const = default; // expected-error >>>> {{return type for defaulted equality comparison operator must be 'bool', >>>> not 'const bool'}} >>>> + friend Bool operator==(const Bad&, const Bad&) = default; // >>>> expected-error {{return type for defaulted equality comparison operator >>>> must be 'bool', not 'Bool'}} >>>> + friend int operator!=(const Bad&, const Bad&) = default; // >>>> expected-error {{return type for defaulted equality comparison operator >>>> must be 'bool', not 'int'}} >>>> +}; >>>> + >>>> +template<typename T> struct Ugly { >>>> + T operator==(const Ugly&) const = default; // expected-error >>>> {{return type}} >>>> + T operator!=(const Ugly&) const = default; // expected-error >>>> {{return type}} >>>> + friend T operator==(const Ugly&, const Ugly&) = default; // >>>> expected-error {{return type}} >>>> + friend T operator!=(const Ugly&, const Ugly&) = default; // >>>> expected-error {{return type}} >>>> +}; >>>> +template struct Ugly<bool>; >>>> +template struct Ugly<int>; // expected-note {{in instantiation of}} >>>> >>>> diff --git a/clang/test/CXX/class/class.compare/class.rel/p1.cpp >>>> b/clang/test/CXX/class/class.compare/class.rel/p1.cpp >>>> new file mode 100644 >>>> index 000000000000..3797d5f81f56 >>>> --- /dev/null >>>> +++ b/clang/test/CXX/class/class.compare/class.rel/p1.cpp >>>> @@ -0,0 +1,25 @@ >>>> +// RUN: %clang_cc1 -std=c++2a -verify %s >>>> + >>>> +struct Good { >>>> + bool operator<(const Good&) const = default; >>>> + bool operator>(const Good&) const = default; >>>> + friend bool operator<=(const Good&, const Good&) = default; >>>> + friend bool operator>=(const Good&, const Good&) = default; >>>> +}; >>>> + >>>> +enum Bool : bool {}; >>>> +struct Bad { >>>> + bool &operator<(const Bad&) const = default; // expected-error >>>> {{return type for defaulted relational comparison operator must be 'bool', >>>> not 'bool &'}} >>>> + const bool operator>(const Bad&) const = default; // expected-error >>>> {{return type for defaulted relational comparison operator must be 'bool', >>>> not 'const bool'}} >>>> + friend Bool operator<=(const Bad&, const Bad&) = default; // >>>> expected-error {{return type for defaulted relational comparison operator >>>> must be 'bool', not 'Bool'}} >>>> + friend int operator>=(const Bad&, const Bad&) = default; // >>>> expected-error {{return type for defaulted relational comparison operator >>>> must be 'bool', not 'int'}} >>>> +}; >>>> + >>>> +template<typename T> struct Ugly { >>>> + T operator<(const Ugly&) const = default; // expected-error {{return >>>> type}} >>>> + T operator>(const Ugly&) const = default; // expected-error {{return >>>> type}} >>>> + friend T operator<=(const Ugly&, const Ugly&) = default; // >>>> expected-error {{return type}} >>>> + friend T operator>=(const Ugly&, const Ugly&) = default; // >>>> expected-error {{return type}} >>>> +}; >>>> +template struct Ugly<bool>; >>>> +template struct Ugly<int>; // expected-note {{in instantiation of}} >>>> >>>> diff --git >>>> a/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p1.cpp >>>> b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p1.cpp >>>> index 3f2bc569edf6..6e9b45903d39 100644 >>>> --- a/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p1.cpp >>>> +++ b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p1.cpp >>>> @@ -1,12 +1,28 @@ >>>> -// RUN: %clang_cc1 -verify %s -std=c++11 >>>> -// RUN: %clang_cc1 -verify %s -std=c++17 >>>> -// RUN: %clang_cc1 -verify %s -std=c++2a >>>> +// RUN: %clang_cc1 -verify=expected,pre2a %s -std=c++11 >>>> +// RUN: %clang_cc1 -verify=expected,pre2a %s -std=c++17 >>>> +// RUN: %clang_cc1 -verify=expected %s -std=c++2a >>>> >>>> // A function that is explicitly defaulted shall >>>> struct A { >>>> - // -- be a special member function, >>>> - A(int) = default; // expected-error {{only special member functions >>>> may be defaulted}} >>>> + // -- be a special member function [C++2a: or a comparison operator >>>> function], >>>> + A(int) = default; >>>> +#if __cplusplus <= 201703L >>>> + // expected-error@-2 {{only special member functions may be >>>> defaulted}} >>>> +#else >>>> + // expected-error@-4 {{only special member functions and comparison >>>> operators may be defaulted}} >>>> +#endif >>>> A(A) = default; // expected-error {{must pass its first argument by >>>> reference}} >>>> + void f(A) = default; // expected-error-re {{only special member >>>> functions{{( and comparison operators)?}} may be defaulted}} >>>> + >>>> + bool operator==(const A&) const = default; // pre2a-warning >>>> {{defaulted comparison operators are a C++20 extension}} >>>> + bool operator!=(const A&) const = default; // pre2a-warning >>>> {{defaulted comparison operators are a C++20 extension}} >>>> + bool operator<(const A&) const = default; // pre2a-error {{only >>>> special member functions may be defaulted}} >>>> + bool operator>(const A&) const = default; // pre2a-error {{only >>>> special member functions may be defaulted}} >>>> + bool operator<=(const A&) const = default; // pre2a-error {{only >>>> special member functions may be defaulted}} >>>> + bool operator>=(const A&) const = default; // pre2a-error {{only >>>> special member functions may be defaulted}} >>>> + bool operator<=>(const A&) const = default; // pre2a-error 1+{{}} >>>> pre2a-warning {{'<=>' is a single token in C++2a}} >>>> + >>>> + A operator+(const A&) const = default; // expected-error-re {{only >>>> special member functions{{( and comparison operators)?}} may be defaulted}} >>>> >>>> // -- have the same declared function type as if it had been >>>> implicitly >>>> // declared >>>> >>>> diff --git a/clang/test/Parser/cxx0x-decl.cpp >>>> b/clang/test/Parser/cxx0x-decl.cpp >>>> index 2f219ac87fb8..3c1c3602691b 100644 >>>> --- a/clang/test/Parser/cxx0x-decl.cpp >>>> +++ b/clang/test/Parser/cxx0x-decl.cpp >>>> @@ -39,7 +39,7 @@ static_assert(something, ""); // expected-error >>>> {{undeclared identifier}} >>>> >>>> // PR9903 >>>> struct SS { >>>> - typedef void d() = default; // expected-error {{function definition >>>> declared 'typedef'}} expected-error {{only special member functions may be >>>> defaulted}} >>>> + typedef void d() = default; // expected-error {{function definition >>>> declared 'typedef'}} expected-error {{only special member functions and >>>> comparison operators may be defaulted}} >>>> }; >>>> >>>> using PR14855 = int S::; // expected-error {{expected ';' after alias >>>> declaration}} >>>> >>>> diff --git a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp >>>> b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp >>>> index 45a65440d599..c68b7d67932e 100644 >>>> --- a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp >>>> +++ b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp >>>> @@ -175,7 +175,7 @@ namespace PR14577 { >>>> Outer<T>::Inner1<T>::~Inner1() = delete; // expected-error {{nested >>>> name specifier 'Outer<T>::Inner1<T>::' for declaration does not refer into >>>> a class, class template or class template partial specialization}} >>>> expected-error {{only functions can have deleted definitions}} >>>> >>>> template<typename T> >>>> - Outer<T>::Inner2<T>::~Inner2() = default; // expected-error {{nested >>>> name specifier 'Outer<T>::Inner2<T>::' for declaration does not refer into >>>> a class, class template or class template partial specialization}} >>>> expected-error {{only special member functions may be defaulted}} >>>> + Outer<T>::Inner2<T>::~Inner2() = default; // expected-error {{nested >>>> name specifier 'Outer<T>::Inner2<T>::' for declaration does not refer into >>>> a class, class template or class template partial specialization}} >>>> } >>>> >>>> extern "C" { // expected-note {{extern "C" language linkage >>>> specification begins here}} >>>> >>>> diff --git a/clang/test/SemaCXX/cxx17-compat.cpp >>>> b/clang/test/SemaCXX/cxx17-compat.cpp >>>> index 3d5420fa0637..e063b1fc1807 100644 >>>> --- a/clang/test/SemaCXX/cxx17-compat.cpp >>>> +++ b/clang/test/SemaCXX/cxx17-compat.cpp >>>> @@ -88,3 +88,36 @@ void f() { >>>> // expected-warning@-4 {{decomposition declaration declared with >>>> 'static thread_local' specifiers is incompatible with C++ standards before >>>> C++2a}} >>>> #endif >>>> } >>>> + >>>> +struct DefaultedComparisons { >>>> + bool operator==(const DefaultedComparisons&) const = default; >>>> + bool operator!=(const DefaultedComparisons&) const = default; >>>> +#if __cplusplus <= 201703L >>>> + // expected-warning@-3 {{defaulted comparison operators are a C++20 >>>> extension}} >>>> + // expected-warning@-3 {{defaulted comparison operators are a C++20 >>>> extension}} >>>> +#else >>>> + // expected-warning@-6 {{defaulted comparison operators are >>>> incompatible with C++ standards before C++20}} >>>> + // expected-warning@-6 {{defaulted comparison operators are >>>> incompatible with C++ standards before C++20}} >>>> +#endif >>>> + bool operator<=>(const DefaultedComparisons&) const = default; >>>> +#if __cplusplus <= 201703L >>>> + // expected-error@-2 {{'operator<=' cannot be the name of a >>>> variable or data member}} expected-error@-2 0+{{}} expected-warning@-2 >>>> {{}} >>>> +#else >>>> + // expected-warning@-4 {{'<=>' operator is incompatible with C++ >>>> standards before C++2a}} >>>> +#endif >>>> + bool operator<(const DefaultedComparisons&) const = default; >>>> + bool operator<=(const DefaultedComparisons&) const = default; >>>> + bool operator>(const DefaultedComparisons&) const = default; >>>> + bool operator>=(const DefaultedComparisons&) const = default; >>>> +#if __cplusplus <= 201703L >>>> + // expected-error@-5 {{only special member functions}} >>>> + // expected-error@-5 {{only special member functions}} >>>> + // expected-error@-5 {{only special member functions}} >>>> + // expected-error@-5 {{only special member functions}} >>>> +#else >>>> + // expected-warning@-10 {{defaulted comparison operators are >>>> incompatible with C++ standards before C++20}} >>>> + // expected-warning@-10 {{defaulted comparison operators are >>>> incompatible with C++ standards before C++20}} >>>> + // expected-warning@-10 {{defaulted comparison operators are >>>> incompatible with C++ standards before C++20}} >>>> + // expected-warning@-10 {{defaulted comparison operators are >>>> incompatible with C++ standards before C++20}} >>>> +#endif >>>> +}; >>>> >>>> >>>> >>>> _______________________________________________ >>>> cfe-commits mailing list >>>> cfe-commits@lists.llvm.org >>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >>>> >>> _______________________________________________ >>> cfe-commits mailing list >>> cfe-commits@lists.llvm.org >>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >>> >> _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits