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