Skip to content

Commit 974c8b7

Browse files
committed
[c++20] Add rewriting from comparison operators to <=> / ==.
This adds support for rewriting <, >, <=, and >= to a normal or reversed call to operator<=>, for rewriting != to a normal or reversed call to operator==, and for rewriting <=> and == to reversed forms of those same operators. Note that this is a breaking change for various C++17 code patterns, including some in use in LLVM. The most common patterns (where an operator== becomes ambiguous with a reversed form of itself) are still accepted under this patch, as an extension (with a warning). I'm hopeful that we can get the language rules fixed before C++20 ships, and the extension warning is aimed primarily at providing data to inform that decision. llvm-svn: 375306
1 parent 778dc0f commit 974c8b7

File tree

21 files changed

+966
-199
lines changed

21 files changed

+966
-199
lines changed

clang/include/clang/AST/ExprCXX.h

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,40 @@ class CXXMemberCallExpr final : public CallExpr {
220220
}
221221
};
222222

223+
/// Represents a call to a CUDA kernel function.
224+
class CUDAKernelCallExpr final : public CallExpr {
225+
friend class ASTStmtReader;
226+
227+
enum { CONFIG, END_PREARG };
228+
229+
// CUDAKernelCallExpr has some trailing objects belonging
230+
// to CallExpr. See CallExpr for the details.
231+
232+
CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,
233+
QualType Ty, ExprValueKind VK, SourceLocation RP,
234+
unsigned MinNumArgs);
235+
236+
CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);
237+
238+
public:
239+
static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,
240+
CallExpr *Config, ArrayRef<Expr *> Args,
241+
QualType Ty, ExprValueKind VK,
242+
SourceLocation RP, unsigned MinNumArgs = 0);
243+
244+
static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,
245+
unsigned NumArgs, EmptyShell Empty);
246+
247+
const CallExpr *getConfig() const {
248+
return cast_or_null<CallExpr>(getPreArg(CONFIG));
249+
}
250+
CallExpr *getConfig() { return cast_or_null<CallExpr>(getPreArg(CONFIG)); }
251+
252+
static bool classof(const Stmt *T) {
253+
return T->getStmtClass() == CUDAKernelCallExprClass;
254+
}
255+
};
256+
223257
/// A rewritten comparison expression that was originally written using
224258
/// operator syntax.
225259
///
@@ -310,40 +344,6 @@ class CXXRewrittenBinaryOperator : public Expr {
310344
}
311345
};
312346

313-
/// Represents a call to a CUDA kernel function.
314-
class CUDAKernelCallExpr final : public CallExpr {
315-
friend class ASTStmtReader;
316-
317-
enum { CONFIG, END_PREARG };
318-
319-
// CUDAKernelCallExpr has some trailing objects belonging
320-
// to CallExpr. See CallExpr for the details.
321-
322-
CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,
323-
QualType Ty, ExprValueKind VK, SourceLocation RP,
324-
unsigned MinNumArgs);
325-
326-
CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);
327-
328-
public:
329-
static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,
330-
CallExpr *Config, ArrayRef<Expr *> Args,
331-
QualType Ty, ExprValueKind VK,
332-
SourceLocation RP, unsigned MinNumArgs = 0);
333-
334-
static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,
335-
unsigned NumArgs, EmptyShell Empty);
336-
337-
const CallExpr *getConfig() const {
338-
return cast_or_null<CallExpr>(getPreArg(CONFIG));
339-
}
340-
CallExpr *getConfig() { return cast_or_null<CallExpr>(getPreArg(CONFIG)); }
341-
342-
static bool classof(const Stmt *T) {
343-
return T->getStmtClass() == CUDAKernelCallExprClass;
344-
}
345-
};
346-
347347
/// Abstract class common to all of the C++ "named"/"keyword" casts.
348348
///
349349
/// This abstract class is inherited by all of the classes

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3769,7 +3769,8 @@ def note_ovl_too_many_candidates : Note<
37693769
"pass -fshow-overloads=all to show them">;
37703770

37713771
def select_ovl_candidate_kind : TextSubstitution<
3772-
"%select{function|function|constructor|"
3772+
"%select{function|function|function (with reversed parameter order)|"
3773+
"constructor|"
37733774
"constructor (the implicit default constructor)|"
37743775
"constructor (the implicit copy constructor)|"
37753776
"constructor (the implicit move constructor)|"
@@ -3990,13 +3991,23 @@ def err_ovl_ambiguous_oper_unary : Error<
39903991
"use of overloaded operator '%0' is ambiguous (operand type %1)">;
39913992
def err_ovl_ambiguous_oper_binary : Error<
39923993
"use of overloaded operator '%0' is ambiguous (with operand types %1 and %2)">;
3994+
def ext_ovl_ambiguous_oper_binary_reversed : ExtWarn<
3995+
"ISO C++20 considers use of overloaded operator '%0' (with operand types %1 "
3996+
"and %2) to be ambiguous despite there being a unique best viable function">,
3997+
InGroup<DiagGroup<"ambiguous-reversed-operator">>, SFINAEFailure;
3998+
def note_ovl_ambiguous_oper_binary_reversed_candidate : Note<
3999+
"ambiguity is between a regular call to this operator and a call with the "
4000+
"argument order reversed">;
39934001
def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">;
39944002
def note_assign_lhs_incomplete : Note<"type %0 is incomplete">;
39954003
def err_ovl_deleted_oper : Error<
39964004
"overload resolution selected deleted operator '%0'">;
39974005
def err_ovl_deleted_special_oper : Error<
39984006
"object of type %0 cannot be %select{constructed|copied|moved|assigned|"
39994007
"assigned|destroyed}1 because its %sub{select_special_member_kind}1 is implicitly deleted">;
4008+
def err_ovl_rewrite_equalequal_not_bool : Error<
4009+
"return type %0 of selected 'operator==' function for rewritten "
4010+
"'%1' comparison is not 'bool'">;
40004011
def err_ovl_no_viable_subscript :
40014012
Error<"no viable overloaded operator[] for type %0">;
40024013
def err_ovl_no_oper :
@@ -9961,6 +9972,8 @@ def err_std_compare_type_not_supported : Error<
99619972
"member '%2' is missing|"
99629973
"the type is not trivially copyable|"
99639974
"the type does not have the expected form}1">;
9975+
def note_rewriting_operator_as_spaceship : Note<
9976+
"while rewriting comparison as call to 'operator<=>' declared here">;
99649977

99659978
// Memory Tagging Extensions (MTE) diagnostics
99669979
def err_memtag_arg_null_or_pointer : Error<

clang/include/clang/Basic/OperatorKinds.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ enum OverloadedOperatorKind : int {
3030
/// the preceding "operator" keyword.
3131
const char *getOperatorSpelling(OverloadedOperatorKind Operator);
3232

33+
/// Get the other overloaded operator that the given operator can be rewritten
34+
/// into, if any such operator exists.
35+
inline OverloadedOperatorKind
36+
getRewrittenOverloadedOperator(OverloadedOperatorKind Kind) {
37+
switch (Kind) {
38+
case OO_Less:
39+
case OO_LessEqual:
40+
case OO_Greater:
41+
case OO_GreaterEqual:
42+
return OO_Spaceship;
43+
44+
case OO_ExclaimEqual:
45+
return OO_EqualEqual;
46+
47+
default:
48+
return OO_None;
49+
}
50+
}
51+
3352
} // end namespace clang
3453

3554
#endif

clang/include/clang/Sema/Overload.h

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,30 @@ class Sema;
7171
OCD_ViableCandidates
7272
};
7373

74+
/// The parameter ordering that will be used for the candidate. This is
75+
/// used to represent C++20 binary operator rewrites that reverse the order
76+
/// of the arguments. If the parameter ordering is Reversed, the Args list is
77+
/// reversed (but obviously the ParamDecls for the function are not).
78+
///
79+
/// After forming an OverloadCandidate with reversed parameters, the list
80+
/// of conversions will (as always) be indexed by argument, so will be
81+
/// in reverse parameter order.
82+
enum class OverloadCandidateParamOrder : char { Normal, Reversed };
83+
84+
/// The kinds of rewrite we perform on overload candidates. Note that the
85+
/// values here are chosen to serve as both bitflags and as a rank (lower
86+
/// values are preferred by overload resolution).
87+
enum OverloadCandidateRewriteKind : unsigned {
88+
/// Candidate is not a rewritten candidate.
89+
CRK_None = 0x0,
90+
91+
/// Candidate is a rewritten candidate with a different operator name.
92+
CRK_DifferentOperator = 0x1,
93+
94+
/// Candidate is a rewritten candidate with a reversed order of parameters.
95+
CRK_Reversed = 0x2,
96+
};
97+
7498
/// ImplicitConversionKind - The kind of implicit conversion used to
7599
/// convert an argument to a parameter's type. The enumerator values
76100
/// match with the table titled 'Conversions' in [over.ics.scs] and are listed
@@ -757,7 +781,8 @@ class Sema;
757781
CXXConversionDecl *Surrogate;
758782

759783
/// The conversion sequences used to convert the function arguments
760-
/// to the function parameters.
784+
/// to the function parameters. Note that these are indexed by argument,
785+
/// so may not match the parameter order of Function.
761786
ConversionSequenceList Conversions;
762787

763788
/// The FixIt hints which can be used to fix the Bad candidate.
@@ -783,6 +808,9 @@ class Sema;
783808
/// True if the candidate was found using ADL.
784809
CallExpr::ADLCallKind IsADLCandidate : 1;
785810

811+
/// Whether this is a rewritten candidate, and if so, of what kind?
812+
OverloadCandidateRewriteKind RewriteKind : 2;
813+
786814
/// FailureKind - The reason why this candidate is not viable.
787815
/// Actually an OverloadFailureKind.
788816
unsigned char FailureKind;
@@ -838,7 +866,8 @@ class Sema;
838866

839867
private:
840868
friend class OverloadCandidateSet;
841-
OverloadCandidate() : IsADLCandidate(CallExpr::NotADL) {}
869+
OverloadCandidate()
870+
: IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {}
842871
};
843872

844873
/// OverloadCandidateSet - A set of overload candidates, used in C++
@@ -867,16 +896,62 @@ class Sema;
867896
CSK_InitByConstructor,
868897
};
869898

899+
/// Information about operator rewrites to consider when adding operator
900+
/// functions to a candidate set.
901+
struct OperatorRewriteInfo {
902+
OperatorRewriteInfo()
903+
: OriginalOperator(OO_None), AllowRewrittenCandidates(false) {}
904+
OperatorRewriteInfo(OverloadedOperatorKind Op, bool AllowRewritten)
905+
: OriginalOperator(Op), AllowRewrittenCandidates(AllowRewritten) {}
906+
907+
/// The original operator as written in the source.
908+
OverloadedOperatorKind OriginalOperator;
909+
/// Whether we should include rewritten candidates in the overload set.
910+
bool AllowRewrittenCandidates;
911+
912+
/// Would use of this function result in a rewrite using a different
913+
/// operator?
914+
bool isRewrittenOperator(const FunctionDecl *FD) {
915+
return OriginalOperator &&
916+
FD->getDeclName().getCXXOverloadedOperator() != OriginalOperator;
917+
}
918+
919+
bool isAcceptableCandidate(const FunctionDecl *FD) {
920+
return AllowRewrittenCandidates || !isRewrittenOperator(FD);
921+
}
922+
923+
/// Determine the kind of rewrite that should be performed for this
924+
/// candidate.
925+
OverloadCandidateRewriteKind
926+
getRewriteKind(const FunctionDecl *FD, OverloadCandidateParamOrder PO) {
927+
OverloadCandidateRewriteKind CRK = CRK_None;
928+
if (isRewrittenOperator(FD))
929+
CRK = OverloadCandidateRewriteKind(CRK | CRK_DifferentOperator);
930+
if (PO == OverloadCandidateParamOrder::Reversed)
931+
CRK = OverloadCandidateRewriteKind(CRK | CRK_Reversed);
932+
return CRK;
933+
}
934+
935+
/// Determine whether we should consider looking for and adding reversed
936+
/// candidates for operator Op.
937+
bool shouldAddReversed(OverloadedOperatorKind Op);
938+
939+
/// Determine whether we should add a rewritten candidate for \p FD with
940+
/// reversed parameter order.
941+
bool shouldAddReversed(ASTContext &Ctx, const FunctionDecl *FD);
942+
};
943+
870944
private:
871945
SmallVector<OverloadCandidate, 16> Candidates;
872-
llvm::SmallPtrSet<Decl *, 16> Functions;
946+
llvm::SmallPtrSet<uintptr_t, 16> Functions;
873947

874948
// Allocator for ConversionSequenceLists. We store the first few of these
875949
// inline to avoid allocation for small sets.
876950
llvm::BumpPtrAllocator SlabAllocator;
877951

878952
SourceLocation Loc;
879953
CandidateSetKind Kind;
954+
OperatorRewriteInfo RewriteInfo;
880955

881956
constexpr static unsigned NumInlineBytes =
882957
24 * sizeof(ImplicitConversionSequence);
@@ -915,19 +990,24 @@ class Sema;
915990
void destroyCandidates();
916991

917992
public:
918-
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
919-
: Loc(Loc), Kind(CSK) {}
993+
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
994+
OperatorRewriteInfo RewriteInfo = {})
995+
: Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {}
920996
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
921997
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
922998
~OverloadCandidateSet() { destroyCandidates(); }
923999

9241000
SourceLocation getLocation() const { return Loc; }
9251001
CandidateSetKind getKind() const { return Kind; }
1002+
OperatorRewriteInfo getRewriteInfo() const { return RewriteInfo; }
9261003

9271004
/// Determine when this overload candidate will be new to the
9281005
/// overload set.
929-
bool isNewCandidate(Decl *F) {
930-
return Functions.insert(F->getCanonicalDecl()).second;
1006+
bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO =
1007+
OverloadCandidateParamOrder::Normal) {
1008+
uintptr_t Key = reinterpret_cast<uintptr_t>(F->getCanonicalDecl());
1009+
Key |= static_cast<uintptr_t>(PO);
1010+
return Functions.insert(Key).second;
9311011
}
9321012

9331013
/// Clear out all of the candidates.

0 commit comments

Comments
 (0)