[clang] d052a57 - [c++2a] Allow comparison functions to be explicitly defaulted.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 12 13:08:27 PDT 2020


I believe the case you're describing is one like this:

struct A { virtual void f() = delete; };
template<typename T> struct B : A { virtual void f() = delete; };
B<int> b;

Fixed in llvmorg-11-init-5618-g9975dc38bf7.

On Tue, 10 Mar 2020 at 18:01, Hubert Tong via cfe-commits <
cfe-commits at lists.llvm.org> wrote:

> On Tue, Mar 10, 2020 at 7:35 PM Richard Smith <richard at metafoo.co.uk>
> wrote:
>
>> Should be fixed in llvmorg-11-init-5426-g4cba668ac13.
>>
> Thanks. We're also seeing a failure on valid code where a template class
> explicitly deletes a function that is an override of an explicitly deleted
> virtual function in a (non-templated) base class.
>
> <stdin>:7:16: error: non-deleted function 'f' cannot override a deleted
> function
>   virtual void f() = delete;
>                ^
> <stdin>:10:8: note: in instantiation of template class 'B<int>' requested
> here
> B<int> b() { return {}; }
>        ^
> <stdin>:2:16: note: overridden virtual function is here
>   virtual void f() = delete;
>                ^
> 1 error generated.
>
>
>>
>> On Sat, 7 Mar 2020 at 08:05, Hubert Tong via cfe-commits <
>> cfe-commits at lists.llvm.org> wrote:
>>
>>> Following this commit, the error recovery for invalid cases that
>>> explicitly define (out-of-line) a member function template as deleted and
>>> attempts to instantiate said function appears broken.
>>>
>>> <stdin>:4:35: error: deleted definition must be first declaration
>>> template <typename> void A::f() = delete;
>>>                                   ^
>>> <stdin>:2:35: note: previous declaration is here
>>>   template <typename> static void f();
>>>                                   ^
>>> clang:
>>> /src_d052a578de58cbbb638cbe2dba05242d1ff443b9/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp:4288:
>>> void clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation,
>>> clang::FunctionDecl *, bool, bool, bool): Assertion `(Pattern ||
>>> PatternDecl->isDefaulted() || PatternDecl->hasSkippedBody()) && "unexpected
>>> kind of function template definition"' failed.
>>> Stack dump:
>>> 0.      Program arguments:
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang -cc1 -std=c++11
>>> -xc++ -
>>> 1.      <stdin>:5:26: current parser token ';'
>>>  #0 0x00003fff7fe6a024 PrintStackTraceSignalHandler(void*)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn+0x1ea024)
>>>  #1 0x00003fff7fe670c8 llvm::sys::RunSignalHandlers()
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn+0x1e70c8)
>>>  #2 0x00003fff7fe6a49c SignalHandler(int)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn+0x1ea49c)
>>>  #3 0x00003fff82030478  0x478 abort
>>>  #4 0x00003fff82030478
>>>  #5 0x00003fff82030478 __assert_fail_base (+0x478)
>>>  #6 0x00003fff7e0a1f94 __assert_fail (/lib64/libc.so.6+0x41f94)
>>>  #7 0x00003fff7e0955d4
>>> clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation,
>>> clang::FunctionDecl*, bool, bool, bool) (/lib64/libc.so.6+0x355d4)
>>>  #8 0x00003fff7e0956c4
>>> clang::Sema::ActOnExplicitInstantiation(clang::Scope*,
>>> clang::SourceLocation, clang::SourceLocation, clang::Declarator&)
>>> (/lib64/libc.so.6+0x356c4)
>>>  #9 0x00003fff7c28d604
>>> clang::Parser::ParseDeclarationAfterDeclaratorAndAttributes(clang::Declarator&,
>>> clang::Parser::ParsedTemplateInfo const&, clang::Parser::ForRangeInit*)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn+0x8ad604)
>>> #10 0x00003fff7c15c2b0
>>> clang::Parser::ParseDeclarationAfterDeclarator(clang::Declarator&,
>>> clang::Parser::ParsedTemplateInfo const&)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn+0x77c2b0)
>>> #11 0x00003fff7c4cc8f8
>>> clang::Parser::ParseSingleDeclarationAfterTemplate(clang::DeclaratorContext,
>>> clang::Parser::ParsedTemplateInfo const&, clang::ParsingDeclRAIIObject&,
>>> clang::SourceLocation&, clang::ParsedAttributes&, clang::AccessSpecifier)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x4c8f8)
>>> #12 0x00003fff7c4cdf48
>>> clang::Parser::ParseExplicitInstantiation(clang::DeclaratorContext,
>>> clang::SourceLocation, clang::SourceLocation, clang::SourceLocation&,
>>> clang::ParsedAttributes&, clang::AccessSpecifier)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x4df48)
>>> #13 0x00003fff7c57c1f0
>>> clang::Parser::ParseDeclarationStartingWithTemplate(clang::DeclaratorContext,
>>> clang::SourceLocation&, clang::ParsedAttributes&, clang::AccessSpecifier)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0xfc1f0)
>>> #14 0x00003fff7c57a6c0
>>> clang::Parser::ParseDeclaration(clang::DeclaratorContext,
>>> clang::SourceLocation&, clang::Parser::ParsedAttributesWithRange&,
>>> clang::SourceLocation*)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0xfa6c0)
>>> #15 0x00003fff7c57a4f8
>>> clang::Parser::ParseExternalDeclaration(clang::Parser::ParsedAttributesWithRange&,
>>> clang::ParsingDeclSpec*)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0xfa4f8)
>>> #16 0x00003fff7c4c5db0
>>> clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&,
>>> bool)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x45db0)
>>> #17 0x00003fff7c58fffc clang::ParseAST(clang::Sema&, bool, bool)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x10fffc)
>>> #18 0x00003fff7c58def4 clang::ASTFrontendAction::ExecuteAction()
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x10def4)
>>> #19 0x00003fff7c4b01e0 clang::FrontendAction::Execute()
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn+0x301e0)
>>> #20 0x00003fff7e93d57c
>>> clang::CompilerInstance::ExecuteAction(clang::FrontendAction&)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn+0x10d57c)
>>> #21 0x00003fff7e93cbf0
>>> clang::ExecuteCompilerInvocation(clang::CompilerInstance*)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn+0x10cbf0)
>>> #22 0x00003fff7e8d5bd4 cc1_main(llvm::ArrayRef<char const*>, char
>>> const*, void*)
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn+0xa5bd4)
>>> #23 0x00003fff7e8042f0 main
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontendTool.so.10svn+0x42f0)
>>> #24 0x0000000010012594 generic_start_main.isra.0
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang+0x10012594)
>>> #25 0x000000001000f37c __libc_start_main
>>> (/build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang+0x1000f37c)
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn(+0x1ea024)[0x3fff7fe6a024]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn(_ZN4llvm3sys17RunSignalHandlersEv+0xc8)[0x3fff7fe670c8]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libLLVMSupport.so.10svn(+0x1ea49c)[0x3fff7fe6a49c]
>>> [0x3fff82030478]
>>> /lib64/libc.so.6(abort+0x2b4)[0x3fff7e0a1f94]
>>> /lib64/libc.so.6(+0x355d4)[0x3fff7e0955d4]
>>> /lib64/libc.so.6(__assert_fail+0x64)[0x3fff7e0956c4]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn(_ZN5clang4Sema29InstantiateFunctionDefinitionENS_14SourceLocationEPNS_12FunctionDeclEbbb+0x1244)[0x3fff7c28d604]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangSema.so.10svn(_ZN5clang4Sema26ActOnExplicitInstantiationEPNS_5ScopeENS_14SourceLocationES3_RNS_10DeclaratorE+0x2290)[0x3fff7c15c2b0]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser44ParseDeclarationAfterDeclaratorAndAttributesERNS_10DeclaratorERKNS0_18ParsedTemplateInfoEPNS0_12ForRangeInitE+0xf8)[0x3fff7c4cc8f8]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser31ParseDeclarationAfterDeclaratorERNS_10DeclaratorERKNS0_18ParsedTemplateInfoE+0x98)[0x3fff7c4cdf48]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser35ParseSingleDeclarationAfterTemplateENS_17DeclaratorContextERKNS0_18ParsedTemplateInfoERNS_21ParsingDeclRAIIObjectERNS_14SourceLocationERNS_16ParsedAttributesENS_15AccessSpecifierE+0x950)[0x3fff7c57c1f0]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser26ParseExplicitInstantiationENS_17DeclaratorContextENS_14SourceLocationES2_RS2_RNS_16ParsedAttributesENS_15AccessSpecifierE+0x80)[0x3fff7c57a6c0]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser36ParseDeclarationStartingWithTemplateENS_17DeclaratorContextERNS_14SourceLocationERNS_16ParsedAttributesENS_15AccessSpecifierE+0x158)[0x3fff7c57a4f8]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser16ParseDeclarationENS_17DeclaratorContextERNS_14SourceLocationERNS0_25ParsedAttributesWithRangeEPS2_+0x350)[0x3fff7c4c5db0]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser24ParseExternalDeclarationERNS0_25ParsedAttributesWithRangeEPNS_15ParsingDeclSpecE+0x2bc)[0x3fff7c58fffc]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang6Parser17ParseTopLevelDeclERNS_9OpaquePtrINS_12DeclGroupRefEEEb+0x614)[0x3fff7c58def4]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/../lib/libclangParse.so.10svn(_ZN5clang8ParseASTERNS_4SemaEbb+0x2c0)[0x3fff7c4b01e0]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn(_ZN5clang17ASTFrontendAction13ExecuteActionEv+0xdc)[0x3fff7e93d57c]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn(_ZN5clang14FrontendAction7ExecuteEv+0x150)[0x3fff7e93cbf0]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontend.so.10svn(_ZN5clang16CompilerInstance13ExecuteActionERNS_14FrontendActionE+0x714)[0x3fff7e8d5bd4]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/../lib/libclangFrontendTool.so.10svn(_ZN5clang25ExecuteCompilerInvocationEPNS_16CompilerInstanceE+0x830)[0x3fff7e8042f0]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang(_Z8cc1_mainN4llvm8ArrayRefIPKcEES2_Pv+0x674)[0x10012594]
>>>
>>> /build_d052a578de58cbbb638cbe2dba05242d1ff443b9/bin/clang(main+0x321c)[0x1000f37c]
>>> /lib64/libc.so.6(+0x25100)[0x3fff7e085100]
>>> /lib64/libc.so.6(__libc_start_main+0xc4)[0x3fff7e0852f4]
>>>
>>>
>>> On Tue, Oct 22, 2019 at 9:18 PM Richard Smith via cfe-commits <
>>> cfe-commits at 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 at -2 {{only special member functions may be
>>>> defaulted}}
>>>> +#else
>>>> +  // expected-error at -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 at -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 at -3 {{defaulted comparison operators are a C++20
>>>> extension}}
>>>> +  // expected-warning at -3 {{defaulted comparison operators are a C++20
>>>> extension}}
>>>> +#else
>>>> +  // expected-warning at -6 {{defaulted comparison operators are
>>>> incompatible with C++ standards before C++20}}
>>>> +  // expected-warning at -6 {{defaulted comparison operators are
>>>> incompatible with C++ standards before C++20}}
>>>> +#endif
>>>> +  bool operator<=>(const DefaultedComparisons&) const = default;
>>>> +#if __cplusplus <= 201703L
>>>> +  // expected-error at -2 {{'operator<=' cannot be the name of a
>>>> variable or data member}} expected-error at -2 0+{{}} expected-warning at -2
>>>> {{}}
>>>> +#else
>>>> +  // expected-warning at -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 at -5 {{only special member functions}}
>>>> +  // expected-error at -5 {{only special member functions}}
>>>> +  // expected-error at -5 {{only special member functions}}
>>>> +  // expected-error at -5 {{only special member functions}}
>>>> +#else
>>>> +  // expected-warning at -10 {{defaulted comparison operators are
>>>> incompatible with C++ standards before C++20}}
>>>> +  // expected-warning at -10 {{defaulted comparison operators are
>>>> incompatible with C++ standards before C++20}}
>>>> +  // expected-warning at -10 {{defaulted comparison operators are
>>>> incompatible with C++ standards before C++20}}
>>>> +  // expected-warning at -10 {{defaulted comparison operators are
>>>> incompatible with C++ standards before C++20}}
>>>> +#endif
>>>> +};
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> cfe-commits mailing list
>>>> cfe-commits at lists.llvm.org
>>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>>
>>> _______________________________________________
>>> cfe-commits mailing list
>>> cfe-commits at lists.llvm.org
>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>
>> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200312/46bca708/attachment-0001.html>


More information about the cfe-commits mailing list