[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.
This commit is contained in:
Richard Smith 2019-10-22 17:44:08 -07:00
parent 437e0e5191
commit d052a578de
17 changed files with 522 additions and 100 deletions

View File

@ -59,6 +59,7 @@ class EnumDecl;
class Expr;
class FunctionTemplateDecl;
class FunctionTemplateSpecializationInfo;
class FunctionTypeLoc;
class LabelStmt;
class MemberSpecializationInfo;
class Module;
@ -2362,6 +2363,12 @@ public:
/// 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 different 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();
}

View File

@ -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;

View File

@ -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 "

View File

@ -1237,6 +1237,24 @@ public:
/// 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 @@ public:
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 @@ public:
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
//

View File

@ -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

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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 differently 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;
}
MD->setDefaulted();
MD->setExplicitlyDefaulted();
// 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 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;
// 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;
CheckExplicitlyDefaultedSpecialMember(MD);
if (!MD->isInvalidDecl())
DefineImplicitSpecialMember(*this, MD, DefaultLoc);
} else {
Diag(DefaultLoc, diag::err_default_special_members);
Diag(DefaultLoc, diag::err_default_special_members)
<< getLangOpts().CPlusPlus2a;
return;
}
// 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;
}
if (DefKind.isComparison() &&
!isa<CXXRecordDecl>(FD->getLexicalDeclContext())) {
Diag(FD->getLocation(), diag::err_defaulted_comparison_out_of_class)
<< (int)DefKind.asComparison();
return;
}
// 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);
}
FD->setDefaulted();
FD->setExplicitlyDefaulted();
// Defer checking functions that are defaulted in a dependent context.
if (FD->isDependentContext())
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.
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) {

View File

@ -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.

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}

View File

@ -1,12 +1,28 @@
// RUN: %clang_cc1 -verify %s -std=c++11
// RUN: %clang_cc1 -verify %s -std=c++17
// RUN: %clang_cc1 -verify %s -std=c++2a
// RUN: %clang_cc1 -verify=expected,pre2a %s -std=c++11
// RUN: %clang_cc1 -verify=expected,pre2a %s -std=c++17
// RUN: %clang_cc1 -verify=expected %s -std=c++2a
// A function that is explicitly defaulted shall
struct A {
// -- be a special member function,
A(int) = default; // expected-error {{only special member functions may be defaulted}}
// -- be a special member function [C++2a: or a comparison operator function],
A(int) = default;
#if __cplusplus <= 201703L
// expected-error@-2 {{only special member functions may be defaulted}}
#else
// expected-error@-4 {{only special member functions and comparison operators may be defaulted}}
#endif
A(A) = default; // expected-error {{must pass its first argument by reference}}
void f(A) = default; // expected-error-re {{only special member functions{{( and comparison operators)?}} may be defaulted}}
bool operator==(const A&) const = default; // pre2a-warning {{defaulted comparison operators are a C++20 extension}}
bool operator!=(const A&) const = default; // pre2a-warning {{defaulted comparison operators are a C++20 extension}}
bool operator<(const A&) const = default; // pre2a-error {{only special member functions may be defaulted}}
bool operator>(const A&) const = default; // pre2a-error {{only special member functions may be defaulted}}
bool operator<=(const A&) const = default; // pre2a-error {{only special member functions may be defaulted}}
bool operator>=(const A&) const = default; // pre2a-error {{only special member functions may be defaulted}}
bool operator<=>(const A&) const = default; // pre2a-error 1+{{}} pre2a-warning {{'<=>' is a single token in C++2a}}
A operator+(const A&) const = default; // expected-error-re {{only special member functions{{( and comparison operators)?}} may be defaulted}}
// -- have the same declared function type as if it had been implicitly
// declared

View File

@ -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}}

View File

@ -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}}

View File

@ -88,3 +88,36 @@ void f() {
// expected-warning@-4 {{decomposition declaration declared with 'static thread_local' specifiers is incompatible with C++ standards before C++2a}}
#endif
}
struct DefaultedComparisons {
bool operator==(const DefaultedComparisons&) const = default;
bool operator!=(const DefaultedComparisons&) const = default;
#if __cplusplus <= 201703L
// expected-warning@-3 {{defaulted comparison operators are a C++20 extension}}
// expected-warning@-3 {{defaulted comparison operators are a C++20 extension}}
#else
// expected-warning@-6 {{defaulted comparison operators are incompatible with C++ standards before C++20}}
// expected-warning@-6 {{defaulted comparison operators are incompatible with C++ standards before C++20}}
#endif
bool operator<=>(const DefaultedComparisons&) const = default;
#if __cplusplus <= 201703L
// expected-error@-2 {{'operator<=' cannot be the name of a variable or data member}} expected-error@-2 0+{{}} expected-warning@-2 {{}}
#else
// expected-warning@-4 {{'<=>' operator is incompatible with C++ standards before C++2a}}
#endif
bool operator<(const DefaultedComparisons&) const = default;
bool operator<=(const DefaultedComparisons&) const = default;
bool operator>(const DefaultedComparisons&) const = default;
bool operator>=(const DefaultedComparisons&) const = default;
#if __cplusplus <= 201703L
// expected-error@-5 {{only special member functions}}
// expected-error@-5 {{only special member functions}}
// expected-error@-5 {{only special member functions}}
// expected-error@-5 {{only special member functions}}
#else
// expected-warning@-10 {{defaulted comparison operators are incompatible with C++ standards before C++20}}
// expected-warning@-10 {{defaulted comparison operators are incompatible with C++ standards before C++20}}
// expected-warning@-10 {{defaulted comparison operators are incompatible with C++ standards before C++20}}
// expected-warning@-10 {{defaulted comparison operators are incompatible with C++ standards before C++20}}
#endif
};