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