Skip to content

Commit d052a57

Browse files
committed
[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.
1 parent 437e0e5 commit d052a57

File tree

17 files changed

+517
-95
lines changed

17 files changed

+517
-95
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class EnumDecl;
5959
class Expr;
6060
class FunctionTemplateDecl;
6161
class FunctionTemplateSpecializationInfo;
62+
class FunctionTypeLoc;
6263
class LabelStmt;
6364
class MemberSpecializationInfo;
6465
class Module;
@@ -2362,6 +2363,12 @@ class FunctionDecl : public DeclaratorDecl,
23622363
/// parameters have default arguments (in C++).
23632364
unsigned getMinRequiredArguments() const;
23642365

2366+
/// Find the source location information for how the type of this function
2367+
/// was written. May be absent (for example if the function was declared via
2368+
/// a typedef) and may contain a different type from that of the function
2369+
/// (for example if the function type was adjusted by an attribute).
2370+
FunctionTypeLoc getFunctionTypeLoc() const;
2371+
23652372
QualType getReturnType() const {
23662373
return getType()->castAs<FunctionType>()->getReturnType();
23672374
}

clang/include/clang/Basic/DiagnosticCommonKinds.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ def warn_cxx98_compat_variadic_templates :
8787
Warning<"variadic templates are incompatible with C++98">,
8888
InGroup<CXX98Compat>, DefaultIgnore;
8989
def err_default_special_members : Error<
90-
"only special member functions may be defaulted">;
90+
"only special member functions %select{|and comparison operators }0"
91+
"may be defaulted">;
9192
def err_deleted_non_function : Error<
9293
"only functions can have deleted definitions">;
9394
def err_module_not_found : Error<"module '%0' not found">, DefaultFatal;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8099,6 +8099,31 @@ def note_vbase_moved_here : Note<
80998099
"%select{%1 is a virtual base class of base class %2 declared here|"
81008100
"virtual base class %1 declared here}0">;
81018101

8102+
// C++20 defaulted comparisons
8103+
// This corresponds to values of Sema::DefaultedComparisonKind.
8104+
def select_defaulted_comparison_kind : TextSubstitution<
8105+
"%select{<ERROR>|equality|three-way|equality|relational}0 comparison "
8106+
"operator">;
8107+
def ext_defaulted_comparison : ExtWarn<
8108+
"defaulted comparison operators are a C++20 extension">, InGroup<CXX2a>;
8109+
def warn_cxx17_compat_defaulted_comparison : Warning<
8110+
"defaulted comparison operators are incompatible with C++ standards "
8111+
"before C++20">, InGroup<CXXPre2aCompat>, DefaultIgnore;
8112+
def err_defaulted_comparison_template : Error<
8113+
"comparison operator template cannot be defaulted">;
8114+
def err_defaulted_comparison_out_of_class : Error<
8115+
"%sub{select_defaulted_comparison_kind}0 can only be defaulted in a class "
8116+
"definition">;
8117+
def err_defaulted_comparison_param : Error<
8118+
"invalid parameter type for defaulted %sub{select_defaulted_comparison_kind}0"
8119+
"%diff{; found $, expected $|}1,2">;
8120+
def err_defaulted_comparison_non_const : Error<
8121+
"defaulted member %sub{select_defaulted_comparison_kind}0 must be "
8122+
"const-qualified">;
8123+
def err_defaulted_comparison_return_type_not_bool : Error<
8124+
"return type for defaulted %sub{select_defaulted_comparison_kind}0 "
8125+
"must be 'bool', not %1">;
8126+
81028127
def ext_implicit_exception_spec_mismatch : ExtWarn<
81038128
"function previously declared with an %select{explicit|implicit}0 exception "
81048129
"specification redeclared with an %select{implicit|explicit}0 exception "

clang/include/clang/Sema/Sema.h

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,24 @@ class Sema {
12371237
/// same special member, we should act as if it is not yet declared.
12381238
llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
12391239

1240+
/// Kinds of defaulted comparison operator functions.
1241+
enum class DefaultedComparisonKind {
1242+
/// This is not a defaultable comparison operator.
1243+
None,
1244+
/// This is an operator== that should be implemented as a series of
1245+
/// subobject comparisons.
1246+
Equal,
1247+
/// This is an operator<=> that should be implemented as a series of
1248+
/// subobject comparisons.
1249+
ThreeWay,
1250+
/// This is an operator!= that should be implemented as a rewrite in terms
1251+
/// of a == comparison.
1252+
NotEqual,
1253+
/// This is an <, <=, >, or >= that should be implemented as a rewrite in
1254+
/// terms of a <=> comparison.
1255+
Relational,
1256+
};
1257+
12401258
/// The function definitions which were renamed as part of typo-correction
12411259
/// to match their respective declarations. We want to keep track of them
12421260
/// to ensure that we don't emit a "redefinition" error if we encounter a
@@ -2541,7 +2559,52 @@ class Sema {
25412559
bool SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
25422560
TrivialABIHandling TAH = TAH_IgnoreTrivialABI,
25432561
bool Diagnose = false);
2544-
CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD);
2562+
2563+
/// For a defaulted function, the kind of defaulted function that it is.
2564+
class DefaultedFunctionKind {
2565+
CXXSpecialMember SpecialMember : 8;
2566+
DefaultedComparisonKind Comparison : 8;
2567+
2568+
public:
2569+
DefaultedFunctionKind()
2570+
: SpecialMember(CXXInvalid), Comparison(DefaultedComparisonKind::None) {
2571+
}
2572+
DefaultedFunctionKind(CXXSpecialMember CSM)
2573+
: SpecialMember(CSM), Comparison(DefaultedComparisonKind::None) {}
2574+
DefaultedFunctionKind(DefaultedComparisonKind Comp)
2575+
: SpecialMember(CXXInvalid), Comparison(Comp) {}
2576+
2577+
bool isSpecialMember() const { return SpecialMember != CXXInvalid; }
2578+
bool isComparison() const {
2579+
return Comparison != DefaultedComparisonKind::None;
2580+
}
2581+
2582+
explicit operator bool() const {
2583+
return isSpecialMember() || isComparison();
2584+
}
2585+
2586+
CXXSpecialMember asSpecialMember() const { return SpecialMember; }
2587+
DefaultedComparisonKind asComparison() const { return Comparison; }
2588+
2589+
/// Get the index of this function kind for use in diagnostics.
2590+
unsigned getDiagnosticIndex() const {
2591+
static_assert(CXXInvalid > CXXDestructor,
2592+
"invalid should have highest index");
2593+
static_assert((unsigned)DefaultedComparisonKind::None == 0,
2594+
"none should be equal to zero");
2595+
return SpecialMember + (unsigned)Comparison;
2596+
}
2597+
};
2598+
2599+
DefaultedFunctionKind getDefaultedFunctionKind(const FunctionDecl *FD);
2600+
2601+
CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD) {
2602+
return getDefaultedFunctionKind(MD).asSpecialMember();
2603+
}
2604+
DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) {
2605+
return getDefaultedFunctionKind(FD).asComparison();
2606+
}
2607+
25452608
void ActOnLastBitfield(SourceLocation DeclStart,
25462609
SmallVectorImpl<Decl *> &AllIvarDecls);
25472610
Decl *ActOnIvar(Scope *S, SourceLocation DeclStart,
@@ -6361,9 +6424,15 @@ class Sema {
63616424
StorageClass &SC);
63626425
void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD);
63636426

6364-
void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD);
6427+
void CheckExplicitlyDefaultedFunction(FunctionDecl *MD);
6428+
6429+
bool CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
6430+
CXXSpecialMember CSM);
63656431
void CheckDelayedMemberExceptionSpecs();
63666432

6433+
bool CheckExplicitlyDefaultedComparison(FunctionDecl *MD,
6434+
DefaultedComparisonKind DCK);
6435+
63676436
//===--------------------------------------------------------------------===//
63686437
// C++ Derived Classes
63696438
//

clang/lib/AST/Decl.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3322,12 +3322,14 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
33223322
return FoundBody;
33233323
}
33243324

3325-
SourceRange FunctionDecl::getReturnTypeSourceRange() const {
3325+
FunctionTypeLoc FunctionDecl::getFunctionTypeLoc() const {
33263326
const TypeSourceInfo *TSI = getTypeSourceInfo();
3327-
if (!TSI)
3328-
return SourceRange();
3329-
FunctionTypeLoc FTL =
3330-
TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
3327+
return TSI ? TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>()
3328+
: FunctionTypeLoc();
3329+
}
3330+
3331+
SourceRange FunctionDecl::getReturnTypeSourceRange() const {
3332+
FunctionTypeLoc FTL = getFunctionTypeLoc();
33313333
if (!FTL)
33323334
return SourceRange();
33333335

@@ -3343,15 +3345,8 @@ SourceRange FunctionDecl::getReturnTypeSourceRange() const {
33433345
}
33443346

33453347
SourceRange FunctionDecl::getExceptionSpecSourceRange() const {
3346-
const TypeSourceInfo *TSI = getTypeSourceInfo();
3347-
if (!TSI)
3348-
return SourceRange();
3349-
FunctionTypeLoc FTL =
3350-
TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
3351-
if (!FTL)
3352-
return SourceRange();
3353-
3354-
return FTL.getExceptionSpecRange();
3348+
FunctionTypeLoc FTL = getFunctionTypeLoc();
3349+
return FTL ? FTL.getExceptionSpecRange() : SourceRange();
33553350
}
33563351

33573352
/// For an inline function definition in C, or for a gnu_inline function

clang/lib/Parse/ParseDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2349,7 +2349,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
23492349
Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
23502350
<< 0 /* default */;
23512351
else
2352-
Diag(ConsumeToken(), diag::err_default_special_members);
2352+
Diag(ConsumeToken(), diag::err_default_special_members)
2353+
<< getLangOpts().CPlusPlus2a;
23532354
} else {
23542355
InitializerScopeRAII InitScope(*this, D, ThisDecl);
23552356

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2978,7 +2978,8 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
29782978
Diag(Tok, diag::err_default_delete_in_multiple_declaration)
29792979
<< 0 /* default */;
29802980
else
2981-
Diag(ConsumeToken(), diag::err_default_special_members);
2981+
Diag(ConsumeToken(), diag::err_default_special_members)
2982+
<< getLangOpts().CPlusPlus2a;
29822983
return ExprError();
29832984
}
29842985
}

clang/lib/Sema/SemaDecl.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2993,28 +2993,6 @@ struct GNUCompatibleParamWarning {
29932993

29942994
} // end anonymous namespace
29952995

2996-
/// getSpecialMember - get the special member enum for a method.
2997-
Sema::CXXSpecialMember Sema::getSpecialMember(const CXXMethodDecl *MD) {
2998-
if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(MD)) {
2999-
if (Ctor->isDefaultConstructor())
3000-
return Sema::CXXDefaultConstructor;
3001-
3002-
if (Ctor->isCopyConstructor())
3003-
return Sema::CXXCopyConstructor;
3004-
3005-
if (Ctor->isMoveConstructor())
3006-
return Sema::CXXMoveConstructor;
3007-
} else if (isa<CXXDestructorDecl>(MD)) {
3008-
return Sema::CXXDestructor;
3009-
} else if (MD->isCopyAssignmentOperator()) {
3010-
return Sema::CXXCopyAssignment;
3011-
} else if (MD->isMoveAssignmentOperator()) {
3012-
return Sema::CXXMoveAssignment;
3013-
}
3014-
3015-
return Sema::CXXInvalid;
3016-
}
3017-
30182996
// Determine whether the previous declaration was a definition, implicit
30192997
// declaration, or a declaration.
30202998
template <typename T>

0 commit comments

Comments
 (0)