EricWF updated this revision to Diff 146274.
EricWF marked 13 inline comments as done.
EricWF added a comment.
- Change `CXXRewrittenExpr` to be a non-general wrapper only for rewritten
comparison operators.
- Start fixing and improving overload resolution diagnostics.
https://reviews.llvm.org/D45680
Files:
include/clang/AST/ComparisonCategories.h
include/clang/AST/Expr.h
include/clang/AST/ExprCXX.h
include/clang/AST/RecursiveASTVisitor.h
include/clang/AST/Stmt.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/StmtNodes.td
include/clang/Sema/Overload.h
include/clang/Sema/Sema.h
include/clang/Serialization/ASTBitCodes.h
lib/AST/ComparisonCategories.cpp
lib/AST/Expr.cpp
lib/AST/ExprCXX.cpp
lib/AST/ExprClassification.cpp
lib/AST/ExprConstant.cpp
lib/AST/ItaniumMangle.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtProfile.cpp
lib/CodeGen/CGExprScalar.cpp
lib/Sema/SemaExceptionSpec.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/TreeTransform.h
lib/Serialization/ASTReaderStmt.cpp
lib/Serialization/ASTWriterStmt.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
test/CodeGenCXX/cxx2a-compare.cpp
test/SemaCXX/compare-cxx2a.cpp
Index: test/SemaCXX/compare-cxx2a.cpp
===================================================================
--- test/SemaCXX/compare-cxx2a.cpp
+++ test/SemaCXX/compare-cxx2a.cpp
@@ -292,20 +292,21 @@
template <int>
struct Tag {};
-// expected-note@+1 {{candidate}}
-Tag<0> operator<=>(EnumA, EnumA) {
- return {};
+std::strong_ordering operator<=>(EnumA, EnumA) {
+ return std::strong_ordering::equal;
}
-Tag<1> operator<=>(EnumA, EnumB) {
- return {};
+// expected-note@+1 {{candidate function}},
+std::strong_ordering operator<=>(EnumA a, EnumB b) {
+ return ((int)a <=> (int)b);
}
void test_enum_ovl_provided() {
auto r1 = (EnumA::A <=> EnumA::A);
- ASSERT_EXPR_TYPE(r1, Tag<0>);
+ ASSERT_EXPR_TYPE(r1, std::strong_ordering);
auto r2 = (EnumA::A <=> EnumB::B);
- ASSERT_EXPR_TYPE(r2, Tag<1>);
- (void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumA')}}
+ ASSERT_EXPR_TYPE(r2, std::strong_ordering);
+ (void)(EnumB::B <=> EnumA::A); // OK, chooses reverse order synthesized candidate.
+ (void)(EnumB::B <=> EnumC::C); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumC')}}
}
void enum_float_test() {
@@ -421,3 +422,114 @@
}
} // namespace ComplexTest
+
+namespace TestRewritting {
+
+struct T {
+ int x;
+ // expected-note@+1 {{candidate}}
+ constexpr std::strong_ordering operator<=>(T y) const {
+ return (x <=> y.x);
+ }
+};
+
+struct U {
+ int x;
+ // FIXME: This diagnostic is wrong-ish.
+ // expected-note@+1 {{candidate function not viable: requires single argument 'y', but 2 arguments were provided}}
+ constexpr std::strong_equality operator<=>(T y) const {
+ if (x == y.x)
+ return std::strong_equality::equal;
+ return std::strong_equality::nonequal;
+ }
+};
+
+struct X { int x; };
+struct Y { int x; };
+template <int>
+struct Tag {};
+
+Tag<0> operator<=>(X, Y) {
+ return {};
+}
+
+constexpr auto operator<=>(Y y, X x) {
+ return y.x <=> x.x;
+}
+
+void foo() {
+ T t{42};
+ T t2{0};
+ U u{101};
+ auto r1 = (t <=> u);
+ ASSERT_EXPR_TYPE(r1, std::strong_equality);
+ auto r2 = (t <=> t2);
+ ASSERT_EXPR_TYPE(r2, std::strong_ordering);
+
+ auto r3 = t == u;
+ ASSERT_EXPR_TYPE(r3, bool);
+
+ (void)(t < u); // expected-error {{invalid operands to binary expression ('TestRewritting::T' and 'TestRewritting::U')}}
+
+ constexpr X x{1};
+ constexpr Y y{2};
+ constexpr auto r4 = (y < x);
+ static_assert(r4 == false);
+ constexpr auto r5 = (x < y);
+ static_assert(r5 == true);
+}
+
+} // namespace TestRewritting
+
+// The implicit object parameter is not considered when performing partial
+// ordering. That makes the reverse synthesized candidates ambiguous with the
+// rewritten candidates if any of them resolve to a member function.
+namespace TestOvlMatchingIgnoresImplicitObject {
+struct U;
+struct T {
+ std::strong_ordering operator<=>(U const &RHS) const;
+};
+struct U {
+ std::strong_ordering operator<=>(T const &RHS) const = delete;
+};
+
+struct V {
+ int x;
+};
+auto operator<=>(V const &LHS, V &&RHS) { // expected-note 4 {{candidate}}
+ return LHS.x <=> RHS.x;
+}
+auto operator<(V const &, V &&) { // expected-note {{candidate}}
+ return std::strong_equality::equal;
+}
+
+void test() {
+ // OK. selects T::operator<=>(U)
+ (void)(T{} < U{});
+ // expected-error@+1 {{use of overloaded operator '<' is ambiguous}}
+ (void)(V{} < V{});
+ // expected-error@+1 {{use of overloaded operator '<=>' is ambiguous}}
+ (void)(V{} <=> V{});
+}
+
+} // namespace TestOvlMatchingIgnoresImplicitObject
+
+namespace TestRewrittenTemplate {
+
+template <class T>
+auto test(T const &LHS, T const &RHS) {
+ // expected-error@+1 {{invalid operands to binary expression ('const TestRewrittenTemplate::None'}}
+ return LHS < RHS;
+}
+struct None {};
+template auto test<None>(None const &, None const &); // expected-note {{requested here}}
+
+struct Relational {};
+bool operator<(Relational, Relational);
+template auto test<Relational>(Relational const &, Relational const &);
+
+struct ThreeWay {};
+std::strong_ordering operator<=>(ThreeWay, ThreeWay);
+template auto test<ThreeWay>(ThreeWay const &, ThreeWay const &);
+
+} // namespace TestRewrittenTemplate
Index: test/CodeGenCXX/cxx2a-compare.cpp
===================================================================
--- test/CodeGenCXX/cxx2a-compare.cpp
+++ test/CodeGenCXX/cxx2a-compare.cpp
@@ -186,3 +186,17 @@
}
} // namespace ComplexTest
+
+namespace RewrittenTest {
+struct U {
+ int x;
+ std::strong_ordering operator<=>(U const &) const;
+};
+// FIXME(EricWF): Write this test
+auto test(U t1, U t2) {
+ return (t1 < t2);
+}
+
+} // namespace RewrittenTest
+
+
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1289,6 +1289,7 @@
case Stmt::PackExpansionExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
case Stmt::FunctionParmPackExprClass:
+ case Stmt::CXXRewrittenOperatorExprClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoawaitExprClass:
case Stmt::DependentCoawaitExprClass:
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -1695,6 +1695,13 @@
Code = serialization::EXPR_CXX_FOLD;
}
+void ASTStmtWriter::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) {
+ VisitExpr(E);
+ Record.push_back(E->getRewrittenKind());
+ Record.AddStmt(E->getRewrittenExpr());
+ Code = serialization::EXPR_CXX_REWRITTEN_OPERATOR;
+}
+
void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
VisitExpr(E);
Record.AddStmt(E->getSourceExpr());
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -1705,6 +1705,13 @@
E->Opcode = (BinaryOperatorKind)Record.readInt();
}
+void ASTStmtReader::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) {
+ VisitExpr(E);
+ E->setRewrittenKind(
+ static_cast<CXXRewrittenOperatorExpr::RewrittenOpKind>(Record.readInt()));
+ E->Rewritten = Record.readSubExpr();
+}
+
void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
VisitExpr(E);
E->SourceExpr = Record.readSubExpr();
@@ -4074,6 +4081,9 @@
S = new (Context) CXXFoldExpr(Empty);
break;
+ case EXPR_CXX_REWRITTEN_OPERATOR:
+ S = new (Context) CXXRewrittenOperatorExpr(Empty);
+
case EXPR_OPAQUE_VALUE:
S = new (Context) OpaqueValueExpr(Empty);
break;
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -3217,6 +3217,11 @@
return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator);
}
+ ExprResult RebuildCXXRewrittenOperatorExpr(
+ CXXRewrittenOperatorExpr::RewrittenOpKind Kind, Expr *Rewritten) {
+ return new (SemaRef.Context) CXXRewrittenOperatorExpr(Kind, Rewritten);
+ }
+
/// Build a new atomic operation expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -11516,6 +11521,25 @@
}
template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXRewrittenOperatorExpr(
+ CXXRewrittenOperatorExpr *E) {
+
+ // FIXME(EricWF): Is there a case where the underlying expression has been
+ // transformed in such a way that we need to re-compute the rewritten
+ // expression? (and not just re-build it).
+ ExprResult RewrittenRes = getDerived().TransformExpr(E->getRewrittenExpr());
+ if (RewrittenRes.isInvalid())
+ return ExprError();
+ Expr *Rewritten = RewrittenRes.get();
+
+ if (Rewritten == E->getRewrittenExpr() && !getDerived().AlwaysRebuild())
+ return E;
+
+ return getDerived().RebuildCXXRewrittenOperatorExpr(E->getRewrittenKind(),
+ Rewritten);
+}
+
+template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
Expr *Pattern = E->getPattern();
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -831,6 +831,19 @@
}
}
+const ImplicitConversionSequence &
+OverloadCandidate::getConversion(unsigned ArgIdx) const {
+ return Conversions[getConversionIndexForArgIndex(ArgIdx)];
+}
+
+unsigned OverloadCandidate::getConversionIndexForArgIndex(unsigned Idx) const {
+ if (getRewrittenKind() != ROC_AsReversedThreeWay)
+ return Idx;
+ // FIXME(EricWF): Handle these cases.
+ assert(Idx < 2);
+ return Idx == 0 ? 1 : 0;
+}
+
void OverloadCandidateSet::destroyCandidates() {
for (iterator i = begin(), e = end(); i != e; ++i) {
for (auto &C : i->Conversions)
@@ -5942,13 +5955,15 @@
// (possibly cv-qualified) T2", when T2 is an enumeration type, are
// candidate functions.
if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&
- !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))
+ !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args)) {
return;
+ }
// C++11 [class.copy]p11: [DR1402]
// A defaulted move constructor that is defined as deleted is ignored by
// overload resolution.
CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Function);
+
if (Constructor && Constructor->isDefaulted() && Constructor->isDeleted() &&
Constructor->isMoveConstructor())
return;
@@ -8862,20 +8877,125 @@
}
}
+
+/// Add the rewritten and synthesized candidates for binary comparison
+/// operators. No additional semantic checking is done to see if the candidate
+/// is well formed.
+void Sema::AddRewrittenOperatorCandidates(OverloadedOperatorKind Op,
+ SourceLocation OpLoc,
+ ArrayRef<Expr *> InputArgs,
+ const UnresolvedSetImpl &Fns,
+ OverloadCandidateSet &CandidateSet,
+ bool PerformADL) {
+ auto Opc = BinaryOperator::getOverloadedOpcode(Op);
+ bool IsEquality = BinaryOperator::isEqualityOp(Opc);
+ bool IsRelational = BinaryOperator::isRelationalOp(Opc);
+ bool IsRelationalOrEquality = IsEquality || IsRelational;
+ if (!IsRelationalOrEquality && Opc != BO_Cmp)
+ return;
+ assert(InputArgs.size() == 2);
+
+ OverloadedOperatorKind CmpOp = OO_Spaceship;
+ DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(CmpOp);
+
+ // AddCandidates - Add operator<=> candidates for the specified set of args,
+ // and mark all newly generated candidates as having the specified
+ // 'RewrittenOverloadCandidateKind'.
+ auto AddCandidates = [&](ArrayRef<Expr *> Args,
+ RewrittenOverloadCandidateKind Kind) {
+ OverloadCandidateSet::AddingRewrittenCandidateGuard Guard(CandidateSet,
+ Kind);
+
+ unsigned InitialSize = CandidateSet.size();
+ AddFunctionCandidates(Fns, Args, CandidateSet);
+ AddMemberOperatorCandidates(CmpOp, OpLoc, Args, CandidateSet);
+ if (PerformADL)
+ AddArgumentDependentLookupCandidates(OpName, OpLoc, Args,
+ /*ExplicitTemplateArgs*/ nullptr,
+ CandidateSet);
+ AddBuiltinOperatorCandidates(CmpOp, OpLoc, Args, CandidateSet);
+
+ for (auto It = std::next(CandidateSet.begin(), InitialSize);
+ It != CandidateSet.end(); ++It) {
+ OverloadCandidate &Ovl = *It;
+ if (!Ovl.getRewrittenKind()) {
+ if (Ovl.Function)
+ Ovl.Function->dumpColor();
+ }
+ assert(Ovl.getRewrittenKind());
+ if (IsRelationalOrEquality) {
+ if (FunctionDecl *FD = Ovl.Function) {
+ if (FD->getReturnType()->isUndeducedType()) {
+ // FIXME(EricWF): Must the return type already be deduced?
+ // Attempt to write a test case to hit this, otherwise remove it.
+ assert(false);
+ if (DeduceReturnType(FD, OpLoc)) {
+ // FIXME(EricWF): Does deduction failure actually make this
+ // non-vialble? probably not?
+ assert(false);
+ Ovl.Viable = false;
+ continue;
+ }
+ }
+ QualType RetTy = FD->getReturnType();
+ assert(!RetTy->isDependentType());
+ if (const ComparisonCategoryInfo *Info =
+ Context.CompCategories.lookupInfoForType(RetTy)) {
+ if (!Info->isUsableWithOperator(Opc)) {
+ Ovl.Viable = false;
+ Ovl.FailureKind = ovl_rewritten_operand_non_valid_for_operator;
+ continue;
+ }
+ } else {
+ // FIXME(EricWF): Check that the return type can be used with
+ // the specified relational operator
+ }
+ } else {
+ // Attempt to compute the comparison category type for a selecetd
+ // builtin function.
+ Optional<ComparisonCategoryType> CompType =
+ ComparisonCategories::computeComparisonTypeForBuiltin(
+ Ovl.BuiltinParamTypes[0], Ovl.BuiltinParamTypes[1]);
+ if (!CompType ||
+ !ComparisonCategoryInfo::isUsableWithOperator(*CompType, Opc)) {
+ Ovl.Viable = false;
+ Ovl.FailureKind = ovl_rewritten_operand_non_valid_for_operator;
+ continue;
+ }
+ }
+ }
+ }
+ };
+
+ // If we have a relational or equality operation, add the rewritten candidates
+ // of the form: (LHS <=> RHS) @ 0
+ if (IsRelationalOrEquality)
+ AddCandidates(InputArgs, ROC_AsThreeWay);
+
+ // TODO: We should be able to avoid adding synthesized candidates when LHS and
+ // RHS have the same type, value category, and other relevent properties.
+ // In that case synthesized candidates for <=> should be the same as the
+ // rewritten ones. Note: It's still possible for the result of operator<=> to
+ // be usable only on the left or right side of the expression (0 @ <result>)
+ // or (<result> @ 0).
+
+ // For relational, equality, and three-way comparisons, add the rewritten and
+ // synthesized candidates of the form: 0 @ (RHS <=> LHS)
+ SmallVector<Expr *, 2> ReverseArgs(InputArgs.rbegin(), InputArgs.rend());
+ AddCandidates(ReverseArgs, ROC_AsReversedThreeWay);
+}
+
/// Add function candidates found via argument-dependent lookup
/// to the set of overloading candidates.
///
/// This routine performs argument-dependent name lookup based on the
/// given function name (which may also be an operator name) and adds
/// all of the overload candidates found by ADL to the overload
/// candidate set (C++ [basic.lookup.argdep]).
-void
-Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
- SourceLocation Loc,
- ArrayRef<Expr *> Args,
+void Sema::AddArgumentDependentLookupCandidates(
+ DeclarationName Name, SourceLocation Loc, ArrayRef<Expr *> Args,
TemplateArgumentListInfo *ExplicitTemplateArgs,
- OverloadCandidateSet& CandidateSet,
- bool PartialOverloading) {
+ OverloadCandidateSet &CandidateSet, bool PartialOverloading) {
ADLResult Fns;
// FIXME: This approach for uniquing ADL results (and removing
@@ -9008,8 +9128,8 @@
assert(Cand2.Conversions.size() == NumArgs && "Overload candidate mismatch");
bool HasBetterConversion = false;
for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
- bool Cand1Bad = IsIllFormedConversion(Cand1.Conversions[ArgIdx]);
- bool Cand2Bad = IsIllFormedConversion(Cand2.Conversions[ArgIdx]);
+ bool Cand1Bad = IsIllFormedConversion(Cand1.getConversion(ArgIdx));
+ bool Cand2Bad = IsIllFormedConversion(Cand2.getConversion(ArgIdx));
if (Cand1Bad != Cand2Bad) {
if (Cand1Bad)
return false;
@@ -9026,8 +9146,8 @@
// conversion sequence than ICSi(F2), and then...
for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
switch (CompareImplicitConversionSequences(S, Loc,
- Cand1.Conversions[ArgIdx],
- Cand2.Conversions[ArgIdx])) {
+ Cand1.getConversion(ArgIdx),
+ Cand2.getConversion(ArgIdx))) {
case ImplicitConversionSequence::Better:
// Cand1 has a better conversion sequence.
HasBetterConversion = true;
@@ -9133,6 +9253,55 @@
// Inherited from sibling base classes: still ambiguous.
}
+ // Check C++2a tie-breakers for rewritten candidates
+ {
+ // --- F2 is a rewritten candidate ([over.match.oper]) and F1 is not.
+ RewrittenOverloadCandidateKind C1Roc = Cand1.getRewrittenKind();
+ RewrittenOverloadCandidateKind C2Roc = Cand2.getRewrittenKind();
+ if (C1Roc || C2Roc) {
+ if (!C1Roc || !C2Roc)
+ return !C1Roc;
+ // --- F1 and F2 are rewritten candidates, and F2 is a synthesized
+ // candidate with reversed order of parameters and F1 is not.
+ if ((C1Roc == ROC_AsReversedThreeWay ||
+ C2Roc == ROC_AsReversedThreeWay) &&
+ C1Roc != C2Roc) {
+ auto GetParamTypes = [&](const OverloadCandidate &Ovl) {
+ SmallVector<QualType, 2> Types;
+ // If the candidate is a method, compute the implicit object type.
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Ovl.Function)) {
+ assert(Ovl.Conversions[0].isStandard());
+ QualType Ty = Ovl.Conversions[0].Standard.getToType(2);
+ assert(!Ty->isReferenceType());
+ const auto *FTP = MD->getType()->getAs<FunctionProtoType>();
+ switch (FTP->getRefQualifier()) {
+ case RQ_LValue:
+ case RQ_None:
+ Types.push_back(S.Context.getLValueReferenceType(Ty));
+ break;
+ case RQ_RValue:
+ Types.push_back(S.Context.getRValueReferenceType(Ty));
+ break;
+ }
+ }
+ for (unsigned I = 0; I < Ovl.getNumParams(); ++I)
+ Types.push_back(Ovl.getParamType(I).getCanonicalType());
+
+ // Reverse the order of the parameter types if this is the
+ // reverse-ordered overload.
+ assert(Types.size() == 2);
+ if (Ovl.getRewrittenKind() == ROC_AsReversedThreeWay)
+ std::swap(Types[0], Types[1]);
+
+ // Return the final list of types.
+ return Types;
+ };
+ if (GetParamTypes(Cand1) == GetParamTypes(Cand2))
+ return C2Roc == ROC_AsReversedThreeWay;
+ }
+ }
+ }
+
// Check C++17 tie-breakers for deduction guides.
{
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
@@ -9335,12 +9504,17 @@
oc_implicit_copy_assignment,
oc_implicit_move_assignment,
oc_inherited_constructor,
- oc_inherited_constructor_template
+ oc_inherited_constructor_template,
+ oc_rewritten_comparison_operator,
+ oc_synthesized_comparison_operator,
+ oc_rewritten_comparison_operator_template,
+ oc_synthesized_comparison_operator_template,
};
static OverloadCandidateKind
ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,
- std::string &Description) {
+ std::string &Description,
+ RewrittenOverloadCandidateKind ROC = ROC_None) {
bool isTemplate = false;
if (FunctionTemplateDecl *FunTmpl = Fn->getPrimaryTemplate()) {
@@ -9385,6 +9559,13 @@
return oc_method;
}
+ if (ROC == ROC_AsThreeWay)
+ return isTemplate ? oc_rewritten_comparison_operator_template
+ : oc_rewritten_comparison_operator;
+ if (ROC == ROC_AsReversedThreeWay)
+ return isTemplate ? oc_synthesized_comparison_operator_template
+ : oc_synthesized_comparison_operator;
+
return isTemplate ? oc_function_template : oc_function;
}
@@ -9470,22 +9651,30 @@
// Notes the location of an overload candidate.
void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
- QualType DestType, bool TakingAddress) {
+ QualType DestType, bool TakingAddress,
+ RewrittenOverloadCandidateKind ROC) {
if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn))
return;
if (Fn->isMultiVersion() && !Fn->getAttr<TargetAttr>()->isDefaultVersion())
return;
std::string FnDesc;
- OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
+ OverloadCandidateKind K =
+ ClassifyOverloadCandidate(*this, Found, Fn, FnDesc, ROC);
PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
<< (unsigned) K << Fn << FnDesc;
HandleFunctionTypeMismatch(PD, Fn->getType(), DestType);
Diag(Fn->getLocation(), PD);
MaybeEmitInheritedConstructorNote(*this, Found);
}
+void Sema::NoteOverloadCandidate(OverloadCandidate *Cand) {
+ assert(Cand->Function && Cand->FoundDecl);
+ NoteOverloadCandidate(Cand->FoundDecl, Cand->Function, QualType(),
+ /*TakingAddress*/ false, Cand->getRewrittenKind());
+}
+
// Notes the location of all overload candidates designated through
// OverloadedExpr
void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType,
@@ -9553,8 +9742,8 @@
}
std::string FnDesc;
- OverloadCandidateKind FnKind =
- ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
+ OverloadCandidateKind FnKind = ClassifyOverloadCandidate(
+ S, Cand->FoundDecl, Fn, FnDesc, Cand->getRewrittenKind());
Expr *FromExpr = Conv.Bad.FromExpr;
QualType FromTy = Conv.Bad.getFromType();
@@ -10164,6 +10353,10 @@
}
}
+static void DiagnoseFailedRewrittenOperand(Sema &S, OverloadCandidate *Cand) {
+ // FIXME(EricWF): Do something here!
+}
+
static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
FunctionDecl *Callee = Cand->Function;
EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);
@@ -10213,7 +10406,7 @@
}
// We don't really have anything else to say about viable candidates.
- S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
+ S.NoteOverloadCandidate(Cand);
return;
}
@@ -10236,7 +10429,7 @@
case ovl_fail_trivial_conversion:
case ovl_fail_bad_final_conversion:
case ovl_fail_final_conversion_not_exact:
- return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
+ return S.NoteOverloadCandidate(Cand);
case ovl_fail_bad_conversion: {
unsigned I = (Cand->IgnoreObjectArgument ? 1 : 0);
@@ -10247,7 +10440,7 @@
// FIXME: this currently happens when we're called from SemaInit
// when user-conversion overload fails. Figure out how to handle
// those conditions and diagnose them well.
- return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
+ return S.NoteOverloadCandidate(Cand);
}
case ovl_fail_bad_target:
@@ -10279,6 +10472,9 @@
case ovl_non_default_multiversion_function:
// Do nothing, these should simply be ignored.
break;
+ case ovl_rewritten_operand_non_valid_for_operator:
+ DiagnoseFailedRewrittenOperand(S, Cand);
+ break;
}
}
@@ -12228,6 +12424,164 @@
return CreateBuiltinUnaryOp(OpLoc, Opc, Input);
}
+ExprResult Sema::BuildBinaryOperatorCandidate(SourceLocation OpLoc,
+ BinaryOperatorKind Opc,
+ const OverloadCandidate &Ovl,
+ Expr *LHSE, Expr *RHSE,
+ bool HadMultipleCandidates) {
+ Expr *Args[2] = {LHSE, RHSE};
+ OverloadedOperatorKind Op = BinaryOperator::getOverloadedOperator(Opc);
+ // We found a built-in operator or an overloaded operator.
+ FunctionDecl *FnDecl = Ovl.Function;
+
+ if (FnDecl) {
+ Expr *Base = nullptr;
+ // We matched an overloaded operator. Build a call to that
+ // operator.
+
+ // Convert the arguments.
+ if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
+ // Ovl.Access is only meaningful for class members.
+ CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Ovl.FoundDecl);
+
+ ExprResult Arg1 =
+ PerformCopyInitialization(InitializedEntity::InitializeParameter(
+ Context, FnDecl->getParamDecl(0)),
+ SourceLocation(), Args[1]);
+ if (Arg1.isInvalid())
+ return ExprError();
+
+ ExprResult Arg0 = PerformObjectArgumentInitialization(
+ Args[0], /*Qualifier=*/nullptr, Ovl.FoundDecl, Method);
+ if (Arg0.isInvalid())
+ return ExprError();
+ Base = Args[0] = Arg0.getAs<Expr>();
+ Args[1] = Arg1.getAs<Expr>();
+ } else {
+ // Convert the arguments.
+ ExprResult Arg0 =
+ PerformCopyInitialization(InitializedEntity::InitializeParameter(
+ Context, FnDecl->getParamDecl(0)),
+ SourceLocation(), Args[0]);
+ if (Arg0.isInvalid())
+ return ExprError();
+
+ ExprResult Arg1 =
+ PerformCopyInitialization(InitializedEntity::InitializeParameter(
+ Context, FnDecl->getParamDecl(1)),
+ SourceLocation(), Args[1]);
+ if (Arg1.isInvalid())
+ return ExprError();
+ Args[0] = Arg0.getAs<Expr>();
+ Args[1] = Arg1.getAs<Expr>();
+ }
+
+ // Build the actual expression node.
+ ExprResult FnExpr = CreateFunctionRefExpr(
+ *this, FnDecl, Ovl.FoundDecl, Base, HadMultipleCandidates, OpLoc);
+ if (FnExpr.isInvalid())
+ return ExprError();
+
+ // Determine the result type.
+ QualType ResultTy = FnDecl->getReturnType();
+ ExprValueKind VK = Expr::getValueKindForType(ResultTy);
+ ResultTy = ResultTy.getNonLValueExprType(Context);
+
+ CXXOperatorCallExpr *TheCall = new (Context) CXXOperatorCallExpr(
+ Context, Op, FnExpr.get(), Args, ResultTy, VK, OpLoc, FPFeatures);
+
+ if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl))
+ return ExprError();
+
+ ArrayRef<const Expr *> ArgsArray(Args, 2);
+ const Expr *ImplicitThis = nullptr;
+ // Cut off the implicit 'this'.
+ if (isa<CXXMethodDecl>(FnDecl)) {
+ ImplicitThis = ArgsArray[0];
+ ArgsArray = ArgsArray.slice(1);
+ }
+
+ // Check for a self move.
+ if (Op == OO_Equal)
+ DiagnoseSelfMove(Args[0], Args[1], OpLoc);
+
+ checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray,
+ isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),
+ Sema::VariadicDoesNotApply);
+
+ return MaybeBindToTemporary(TheCall);
+
+ } else {
+ // We matched a built-in operator. Convert the arguments, then
+ // break out so that we will build the appropriate built-in
+ // operator node.
+ ExprResult ArgsRes0 =
+ PerformImplicitConversion(Args[0], Ovl.BuiltinParamTypes[0],
+ Ovl.getConversion(0), Sema::AA_Passing);
+ if (ArgsRes0.isInvalid())
+ return ExprError();
+ Args[0] = ArgsRes0.get();
+
+ ExprResult ArgsRes1 =
+ PerformImplicitConversion(Args[1], Ovl.BuiltinParamTypes[1],
+ Ovl.getConversion(1), Sema::AA_Passing);
+ if (ArgsRes1.isInvalid())
+ return ExprError();
+ Args[1] = ArgsRes1.get();
+ }
+ // We matched a built-in operator; build it.
+ return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
+}
+
+static ExprResult BuildRewrittenCandidate(Sema &S, BinaryOperatorKind Opc,
+ const OverloadCandidate &Ovl,
+ ArrayRef<Expr *> Args,
+ const UnresolvedSetImpl &Fns,
+ SourceLocation OpLoc,
+ bool PerformADL) {
+ Expr *RewrittenArgs[2] = {Args[0], Args[1]};
+ assert(Ovl.getRewrittenKind());
+ bool IsSynthesized = Ovl.getRewrittenKind() == ROC_AsReversedThreeWay;
+ if (IsSynthesized)
+ std::swap(RewrittenArgs[0], RewrittenArgs[1]);
+
+ // Supress diagnostics when building the expressions for the specified
+ // candidate. If evaluation fails the candidate will be marked non-viable
+ // and the best viable candidate re-computed.
+ Sema::TentativeAnalysisScope DiagnosticScopeGuard(S);
+
+ // Build the '(LHS <=> RHS)' operand to the full expression.
+ ExprResult RewrittenRes = S.BuildBinaryOperatorCandidate(
+ OpLoc, BO_Cmp, Ovl, RewrittenArgs[0], RewrittenArgs[1],
+ /*HadMultipleCandidates*/ false);
+ if (RewrittenRes.isInvalid())
+ return ExprError();
+
+ if (Opc != BO_Cmp) {
+ // Now attempt to build the full expression '(LHS <=> RHS) @ 0' using the
+ // evaluated operand and the literal 0.
+ llvm::APInt I =
+ llvm::APInt::getNullValue(S.Context.getIntWidth(S.Context.IntTy));
+ Expr *Zero =
+ IntegerLiteral::Create(S.Context, I, S.Context.IntTy, SourceLocation());
+
+ Expr *NewLHS = RewrittenRes.get();
+ Expr *NewRHS = Zero;
+ if (Ovl.getRewrittenKind() == ROC_AsReversedThreeWay)
+ std::swap(NewLHS, NewRHS);
+
+ RewrittenRes =
+ S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL,
+ /*AllowRewrittenCandidates*/ false);
+ if (RewrittenRes.isInvalid())
+ return ExprError();
+ }
+ Expr *Rewritten = RewrittenRes.get();
+
+ return new (S.Context)
+ CXXRewrittenOperatorExpr(Ovl.getRewrittenKind(), Rewritten);
+}
+
/// Create a binary operation that may resolve to an overloaded
/// operator.
///
@@ -12244,11 +12598,11 @@
///
/// \param LHS Left-hand argument.
/// \param RHS Right-hand argument.
-ExprResult
-Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
+ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
BinaryOperatorKind Opc,
- const UnresolvedSetImpl &Fns,
- Expr *LHS, Expr *RHS, bool PerformADL) {
+ const UnresolvedSetImpl &Fns, Expr *LHS,
+ Expr *RHS, bool PerformADL,
+ bool AllowRewrittenCandidates) {
Expr *Args[2] = { LHS, RHS };
LHS=RHS=nullptr; // Please use only Args instead of LHS/RHS couple
@@ -12310,11 +12664,27 @@
if (Opc == BO_PtrMemD)
return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
+ UnresolvedSet<6> OrigFuncs;
+ UnresolvedSet<6> ThreeWayFuncs;
+ for (NamedDecl *D : Fns) {
+ FunctionDecl *FD = D->getAsFunction();
+ if (FD) {
+ assert(FD->isOverloadedOperator());
+ if (FD->getOverloadedOperator() == OO_Spaceship) {
+ ThreeWayFuncs.addDecl(D);
+ if (Op == OO_Spaceship)
+ OrigFuncs.addDecl(D);
+ } else
+ OrigFuncs.addDecl(D);
+ } else
+ OrigFuncs.addDecl(D);
+ }
+
// Build an empty overload set.
OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator);
// Add the candidates from the given function set.
- AddFunctionCandidates(Fns, Args, CandidateSet);
+ AddFunctionCandidates(OrigFuncs, Args, CandidateSet);
// Add operator candidates that are member functions.
AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet);
@@ -12330,119 +12700,28 @@
// Add builtin operator candidates.
AddBuiltinOperatorCandidates(Op, OpLoc, Args, CandidateSet);
- bool HadMultipleCandidates = (CandidateSet.size() > 1);
-
- // Perform overload resolution.
- OverloadCandidateSet::iterator Best;
- switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) {
- case OR_Success: {
- // We found a built-in operator or an overloaded operator.
- FunctionDecl *FnDecl = Best->Function;
-
- if (FnDecl) {
- Expr *Base = nullptr;
- // We matched an overloaded operator. Build a call to that
- // operator.
-
- // Convert the arguments.
- if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
- // Best->Access is only meaningful for class members.
- CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Best->FoundDecl);
-
- ExprResult Arg1 =
- PerformCopyInitialization(
- InitializedEntity::InitializeParameter(Context,
- FnDecl->getParamDecl(0)),
- SourceLocation(), Args[1]);
- if (Arg1.isInvalid())
- return ExprError();
-
- ExprResult Arg0 =
- PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr,
- Best->FoundDecl, Method);
- if (Arg0.isInvalid())
- return ExprError();
- Base = Args[0] = Arg0.getAs<Expr>();
- Args[1] = RHS = Arg1.getAs<Expr>();
- } else {
- // Convert the arguments.
- ExprResult Arg0 = PerformCopyInitialization(
- InitializedEntity::InitializeParameter(Context,
- FnDecl->getParamDecl(0)),
- SourceLocation(), Args[0]);
- if (Arg0.isInvalid())
- return ExprError();
-
- ExprResult Arg1 =
- PerformCopyInitialization(
- InitializedEntity::InitializeParameter(Context,
- FnDecl->getParamDecl(1)),
- SourceLocation(), Args[1]);
- if (Arg1.isInvalid())
- return ExprError();
- Args[0] = LHS = Arg0.getAs<Expr>();
- Args[1] = RHS = Arg1.getAs<Expr>();
+ // C++2a Add rewritten and synthesized operator candidates.
+ bool HasRewrittenCandidates = false;
+ if (getLangOpts().CPlusPlus2a && AllowRewrittenCandidates &&
+ BinaryOperator::isComparisonOp(Opc)) {
+ unsigned BeforeRewrittenSize = CandidateSet.size();
+ AddRewrittenOperatorCandidates(Op, OpLoc, Args, ThreeWayFuncs, CandidateSet,
+ PerformADL);
+ HasRewrittenCandidates = BeforeRewrittenSize != CandidateSet.size();
}
- // Build the actual expression node.
- ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl,
- Best->FoundDecl, Base,
- HadMultipleCandidates, OpLoc);
- if (FnExpr.isInvalid())
- return ExprError();
-
- // Determine the result type.
- QualType ResultTy = FnDecl->getReturnType();
- ExprValueKind VK = Expr::getValueKindForType(ResultTy);
- ResultTy = ResultTy.getNonLValueExprType(Context);
-
- CXXOperatorCallExpr *TheCall =
- new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.get(),
- Args, ResultTy, VK, OpLoc,
- FPFeatures);
-
- if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,
- FnDecl))
- return ExprError();
-
- ArrayRef<const Expr *> ArgsArray(Args, 2);
- const Expr *ImplicitThis = nullptr;
- // Cut off the implicit 'this'.
- if (isa<CXXMethodDecl>(FnDecl)) {
- ImplicitThis = ArgsArray[0];
- ArgsArray = ArgsArray.slice(1);
- }
-
- // Check for a self move.
- if (Op == OO_Equal)
- DiagnoseSelfMove(Args[0], Args[1], OpLoc);
-
- checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray,
- isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),
- VariadicDoesNotApply);
-
- return MaybeBindToTemporary(TheCall);
- } else {
- // We matched a built-in operator. Convert the arguments, then
- // break out so that we will build the appropriate built-in
- // operator node.
- ExprResult ArgsRes0 =
- PerformImplicitConversion(Args[0], Best->BuiltinParamTypes[0],
- Best->Conversions[0], AA_Passing);
- if (ArgsRes0.isInvalid())
- return ExprError();
- Args[0] = ArgsRes0.get();
- ExprResult ArgsRes1 =
- PerformImplicitConversion(Args[1], Best->BuiltinParamTypes[1],
- Best->Conversions[1], AA_Passing);
- if (ArgsRes1.isInvalid())
- return ExprError();
- Args[1] = ArgsRes1.get();
- break;
- }
- }
+ // Perform final overload resolution.
+ bool HadMultipleCandidates = (CandidateSet.size() > 1);
+ OverloadCandidateSet::iterator Best;
+ switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) {
+ case OR_Success:
+ if (Best->getRewrittenKind())
+ return BuildRewrittenCandidate(*this, Opc, *Best, Args, Fns, OpLoc,
+ PerformADL);
+ return BuildBinaryOperatorCandidate(OpLoc, Opc, *Best, Args[0], Args[1],
+ HadMultipleCandidates);
case OR_No_Viable_Function: {
// C++ [over.match.oper]p9:
// If the operator is the operator , [...] and there are no
@@ -12455,15 +12734,15 @@
// operator do not fall through to handling in built-in, but report that
// no overloaded assignment operator found
ExprResult Result = ExprError();
- if (Args[0]->getType()->isRecordType() &&
- Opc >= BO_Assign && Opc <= BO_OrAssign) {
+ if (Args[0]->getType()->isRecordType() && Opc >= BO_Assign &&
+ Opc <= BO_OrAssign) {
Diag(OpLoc, diag::err_ovl_no_viable_oper)
- << BinaryOperator::getOpcodeStr(Opc)
- << Args[0]->getSourceRange() << Args[1]->getSourceRange();
+ << BinaryOperator::getOpcodeStr(Opc) << Args[0]->getSourceRange()
+ << Args[1]->getSourceRange();
if (Args[0]->getType()->isIncompleteType()) {
Diag(OpLoc, diag::note_assign_lhs_incomplete)
- << Args[0]->getType()
- << Args[0]->getSourceRange() << Args[1]->getSourceRange();
+ << Args[0]->getType() << Args[0]->getSourceRange()
+ << Args[1]->getSourceRange();
}
} else {
// This is an erroneous use of an operator which can be overloaded by
@@ -12484,12 +12763,11 @@
BinaryOperator::getOpcodeStr(Opc), OpLoc);
return Result;
}
-
case OR_Ambiguous:
Diag(OpLoc, diag::err_ovl_ambiguous_oper_binary)
- << BinaryOperator::getOpcodeStr(Opc)
- << Args[0]->getType() << Args[1]->getType()
- << Args[0]->getSourceRange() << Args[1]->getSourceRange();
+ << BinaryOperator::getOpcodeStr(Opc) << Args[0]->getType()
+ << Args[1]->getType() << Args[0]->getSourceRange()
+ << Args[1]->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args,
BinaryOperator::getOpcodeStr(Opc), OpLoc);
return ExprError();
@@ -12507,8 +12785,7 @@
return ExprError();
} else {
Diag(OpLoc, diag::err_ovl_deleted_oper)
- << Best->Function->isDeleted()
- << BinaryOperator::getOpcodeStr(Opc)
+ << Best->Function->isDeleted() << BinaryOperator::getOpcodeStr(Opc)
<< getDeletedOrUnavailableSuffix(Best->Function)
<< Args[0]->getSourceRange() << Args[1]->getSourceRange();
}
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -9797,8 +9797,6 @@
ExprResult &LHS,
ExprResult &RHS,
SourceLocation Loc) {
- using CCT = ComparisonCategoryType;
-
QualType LHSType = LHS.get()->getType();
QualType RHSType = RHS.get()->getType();
// Dig out the original argument type and expression before implicit casts
@@ -9865,22 +9863,11 @@
if (HasNarrowing)
return QualType();
- assert(!Type.isNull() && "composite type for <=> has not been set");
+ Optional<ComparisonCategoryType> TypeKind =
+ ComparisonCategories::computeComparisonTypeForBuiltin(Type);
+ assert(TypeKind && "composite type for <=> has not been set");
- auto TypeKind = [&]() {
- if (const ComplexType *CT = Type->getAs<ComplexType>()) {
- if (CT->getElementType()->hasFloatingRepresentation())
- return CCT::WeakEquality;
- return CCT::StrongEquality;
- }
- if (Type->isIntegralOrEnumerationType())
- return CCT::StrongOrdering;
- if (Type->hasFloatingRepresentation())
- return CCT::PartialOrdering;
- llvm_unreachable("other types are unimplemented");
- }();
-
- return S.CheckComparisonCategoryType(TypeKind, Loc);
+ return S.CheckComparisonCategoryType(TypeKind.getValue(), Loc);
}
static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
@@ -9975,32 +9962,12 @@
QualType CompositeTy = LHS.get()->getType();
assert(!CompositeTy->isReferenceType());
- auto buildResultTy = [&](ComparisonCategoryType Kind) {
- return CheckComparisonCategoryType(Kind, Loc);
- };
-
- // C++2a [expr.spaceship]p7: If the composite pointer type is a function
- // pointer type, a pointer-to-member type, or std::nullptr_t, the
- // result is of type std::strong_equality
- if (CompositeTy->isFunctionPointerType() ||
- CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType())
- // FIXME: consider making the function pointer case produce
- // strong_ordering not strong_equality, per P0946R0-Jax18 discussion
- // and direction polls
- return buildResultTy(ComparisonCategoryType::StrongEquality);
-
- // C++2a [expr.spaceship]p8: If the composite pointer type is an object
- // pointer type, p <=> q is of type std::strong_ordering.
- if (CompositeTy->isPointerType()) {
- // P0946R0: Comparisons between a null pointer constant and an object
- // pointer result in std::strong_equality
- if (LHSIsNull != RHSIsNull)
- return buildResultTy(ComparisonCategoryType::StrongEquality);
- return buildResultTy(ComparisonCategoryType::StrongOrdering);
- }
- // C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed.
- // TODO: Extend support for operator<=> to ObjC types.
+ Optional<ComparisonCategoryType> TypeKind =
+ ComparisonCategories::computeComparisonTypeForBuiltin(
+ CompositeTy, LHSIsNull != RHSIsNull);
+ if (!TypeKind)
return InvalidOperands(Loc, LHS, RHS);
+ return CheckComparisonCategoryType(TypeKind.getValue(), Loc);
};
@@ -12370,7 +12337,12 @@
if (Sc && OverOp != OO_None && OverOp != OO_Equal)
S.LookupOverloadedOperatorName(OverOp, Sc, LHS->getType(),
RHS->getType(), Functions);
-
+ if (S.getLangOpts().CPlusPlus2a) {
+ if (Sc && Opc != BO_Cmp && BinaryOperator::isRelationalOp(Opc)) {
+ S.LookupOverloadedOperatorName(OO_Spaceship, Sc, LHS->getType(),
+ RHS->getType(), Functions);
+ }
+ }
// Build the (potentially-overloaded, potentially-dependent)
// binary operation.
return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS);
Index: lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- lib/Sema/SemaExceptionSpec.cpp
+++ lib/Sema/SemaExceptionSpec.cpp
@@ -1174,6 +1174,9 @@
case Expr::VAArgExprClass:
return canSubExprsThrow(*this, E);
+ case Expr::CXXRewrittenOperatorExprClass:
+ return canThrow(cast<CXXRewrittenOperatorExpr>(E)->getRewrittenExpr());
+
// Some might be dependent for other reasons.
case Expr::ArraySubscriptExprClass:
case Expr::OMPArraySectionExprClass:
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -541,6 +541,10 @@
return EmitScalarPrePostIncDec(E, LV, true, true);
}
+ Value *VisitCXXRewrittenOperatorExpr(const CXXRewrittenOperatorExpr *E) {
+ return Visit(E->getRewrittenExpr());
+ }
+
llvm::Value *EmitIncDecConsiderOverflowBehavior(const UnaryOperator *E,
llvm::Value *InVal,
bool IsInc);
Index: lib/AST/StmtProfile.cpp
===================================================================
--- lib/AST/StmtProfile.cpp
+++ lib/AST/StmtProfile.cpp
@@ -1816,6 +1816,13 @@
ID.AddInteger(S->getOperator());
}
+void StmtProfiler::VisitCXXRewrittenOperatorExpr(
+ const CXXRewrittenOperatorExpr *S) {
+ VisitExpr(S);
+ ID.AddInteger(S->getRewrittenKind());
+ VisitExpr(S->getRewrittenExpr());
+}
+
void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) {
VisitStmt(S);
}
Index: lib/AST/StmtPrinter.cpp
===================================================================
--- lib/AST/StmtPrinter.cpp
+++ lib/AST/StmtPrinter.cpp
@@ -2588,6 +2588,15 @@
OS << ")";
}
+void StmtPrinter::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) {
+ // FIXME(EricWF): Are there ever cases where we want to display the rewritten
+ // code? For example when producing diagnostics on implicitly generated
+ // expressions?
+ PrintExpr(E->getOriginalLHS());
+ OS << ' ' << BinaryOperator::getOpcodeStr(E->getOriginalOpcode()) << ' ';
+ PrintExpr(E->getOriginalRHS());
+}
+
// C++ Coroutines TS
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -3516,9 +3516,13 @@
}
// These are used for internal purposes and cannot be meaningfully mangled.
- case Expr::OpaqueValueExprClass:
- llvm_unreachable("cannot mangle opaque value; mangling wrong thing?");
-
+ case Expr::OpaqueValueExprClass: {
+ const OpaqueValueExpr *OVE = cast<OpaqueValueExpr>(E);
+ assert(OVE->getSourceExpr() && "cannot mangle opaque value without a "
+ "source expression; mangling wrong thing?");
+ mangleExpression(OVE->getSourceExpr());
+ break;
+ }
case Expr::InitListExprClass: {
Out << "il";
mangleInitListElements(cast<InitListExpr>(E));
@@ -3559,6 +3563,17 @@
mangleExpression(cast<CXXStdInitializerListExpr>(E)->getSubExpr(), Arity);
break;
+ case Expr::CXXRewrittenOperatorExprClass: {
+ const CXXRewrittenOperatorExpr *RE = cast<CXXRewrittenOperatorExpr>(E);
+ const unsigned NumArgs = 2;
+ auto Operator =
+ BinaryOperator::getOverloadedOperator(RE->getOriginalOpcode());
+ mangleOperatorName(Operator, NumArgs);
+ mangleExpression(RE->getOriginalLHS());
+ mangleExpression(RE->getOriginalRHS());
+ break;
+ }
+
case Expr::SubstNonTypeTemplateParmExprClass:
mangleExpression(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(),
Arity);
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -5059,6 +5059,10 @@
return;
VisitIgnoredValue(E);
}
+
+ bool VisitCXXRewrittenOperatorExpr(const CXXRewrittenOperatorExpr *E) {
+ return StmtVisitorTy::Visit(E->getRewrittenExpr());
+ }
};
} // namespace
@@ -10859,6 +10863,8 @@
case Expr::ChooseExprClass: {
return CheckICE(cast<ChooseExpr>(E)->getChosenSubExpr(), Ctx);
}
+ case Expr::CXXRewrittenOperatorExprClass:
+ return CheckICE(cast<CXXRewrittenOperatorExpr>(E)->getRewrittenExpr(), Ctx);
}
llvm_unreachable("Invalid StmtClass!");
Index: lib/AST/ExprClassification.cpp
===================================================================
--- lib/AST/ExprClassification.cpp
+++ lib/AST/ExprClassification.cpp
@@ -287,7 +287,9 @@
if (cast<GenericSelectionExpr>(E)->isResultDependent())
return Cl::CL_PRValue;
return ClassifyInternal(Ctx,cast<GenericSelectionExpr>(E)->getResultExpr());
-
+ case Expr::CXXRewrittenOperatorExprClass:
+ return ClassifyInternal(
+ Ctx, cast<CXXRewrittenOperatorExpr>(E)->getRewrittenExpr());
case Expr::BinaryOperatorClass:
case Expr::CompoundAssignOperatorClass:
// C doesn't have any binary expressions that are lvalues.
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -1433,3 +1433,100 @@
}
void ArrayTypeTraitExpr::anchor() {}
+
+namespace {
+struct ArgumentExtractor {
+ Expr *Rewritten;
+ bool IsReverseOrder;
+ bool IsThreeWay;
+
+ ArgumentExtractor(const CXXRewrittenOperatorExpr *E)
+ : Rewritten(E->getRewrittenExpr()) {
+ IsReverseOrder = E->getRewrittenKind() == ROC_AsReversedThreeWay;
+ IsThreeWay = getOpcode(Rewritten) == BO_Cmp;
+ }
+
+ static CXXRewrittenOperatorExpr::Opcode getOpcode(Expr *E) {
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+ return BO->getOpcode();
+ if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+ return BinaryOperator::getOverloadedOpcode(CE->getOperator());
+ llvm_unreachable("unhandled case");
+ }
+
+ CXXRewrittenOperatorExpr::Opcode getOriginalOpcode() const {
+ if (IsThreeWay)
+ return BO_Cmp;
+ if (IsReverseOrder)
+ return getOpcode(getRHS(Rewritten));
+ return getOpcode(Rewritten);
+ }
+
+ static Expr *getLHS(Expr *E) {
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+ return BO->getLHS();
+ if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+ return CE->getArg(0);
+ llvm_unreachable("unhandled case");
+ }
+
+ static Expr *getRHS(Expr *E) {
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+ return BO->getRHS();
+ if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+ return CE->getArg(1);
+ llvm_unreachable("unhandled case");
+ }
+
+ Expr *getOriginalLHS() const {
+ if (IsThreeWay) {
+ if (IsReverseOrder)
+ return getRHS(Rewritten);
+ return getLHS(Rewritten);
+ }
+ if (IsReverseOrder)
+ return getRHS(getRHS(Rewritten));
+ return getLHS(getLHS(Rewritten));
+ }
+
+ Expr *getOriginalRHS() const {
+ if (IsThreeWay) {
+ if (IsReverseOrder)
+ return getLHS(Rewritten);
+ return getRHS(Rewritten);
+ }
+ if (IsReverseOrder)
+ return getRHS(getLHS(Rewritten));
+ return getLHS(getRHS(Rewritten));
+ }
+
+ static SourceLocation getOperatorLoc(Expr *E) {
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+ return BO->getOperatorLoc();
+ if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+ return CE->getOperatorLoc();
+ llvm_unreachable("unhandled case");
+ }
+};
+} // namespace
+
+SourceLocation CXXRewrittenOperatorExpr::getOperatorLoc() const {
+ return ArgumentExtractor::getOperatorLoc(this->getRewrittenExpr());
+}
+
+CXXRewrittenOperatorExpr::Opcode CXXRewrittenOperatorExpr::getOpcode() const {
+ return ArgumentExtractor::getOpcode(getRewrittenExpr());
+}
+
+CXXRewrittenOperatorExpr::Opcode
+CXXRewrittenOperatorExpr::getOriginalOpcode() const {
+ return ArgumentExtractor{this}.getOriginalOpcode();
+}
+
+Expr *CXXRewrittenOperatorExpr::getOriginalLHS() const {
+ return ArgumentExtractor{this}.getOriginalLHS();
+}
+
+Expr *CXXRewrittenOperatorExpr::getOriginalRHS() const {
+ return ArgumentExtractor{this}.getOriginalRHS();
+}
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -3188,6 +3188,11 @@
return false;
}
+ case CXXRewrittenOperatorExprClass: {
+ const auto *RO = cast<CXXRewrittenOperatorExpr>(this);
+ return RO->getRewrittenExpr()->HasSideEffects(Ctx, IncludePossibleEffects);
+ }
+
case PseudoObjectExprClass: {
// Only look for side-effects in the semantic form, and look past
// OpaqueValueExpr bindings in that form.
@@ -3898,6 +3903,11 @@
}
}
+OpaqueValueExpr *OpaqueValueExpr::Create(const ASTContext &Ctx, Expr *E) {
+ return new (Ctx) OpaqueValueExpr(E->getExprLoc(), E->getType(),
+ E->getValueKind(), E->getObjectKind(), E);
+}
+
const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) {
if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(e))
e = ewc->getSubExpr();
Index: lib/AST/ComparisonCategories.cpp
===================================================================
--- lib/AST/ComparisonCategories.cpp
+++ lib/AST/ComparisonCategories.cpp
@@ -209,3 +209,132 @@
Values.push_back(CCR::Unordered);
return Values;
}
+
+Optional<ComparisonCategoryType>
+ComparisonCategories::computeComparisonTypeForBuiltin(QualType Ty,
+ bool IsMixedNullCompare) {
+ using CCT = ComparisonCategoryType;
+ if (const ComplexType *CT = Ty->getAs<ComplexType>()) {
+ if (CT->getElementType()->hasFloatingRepresentation())
+ return CCT::WeakEquality;
+ return CCT::StrongEquality;
+ }
+ if (Ty->isIntegralOrEnumerationType())
+ return CCT::StrongOrdering;
+ if (Ty->hasFloatingRepresentation())
+ return CCT::PartialOrdering;
+ // C++2a [expr.spaceship]p7: If the composite pointer type is a function
+ // pointer type, a pointer-to-member type, or std::nullptr_t, the
+ // result is of type std::strong_equality
+ if (Ty->isFunctionPointerType() || Ty->isMemberPointerType() ||
+ Ty->isNullPtrType())
+ // FIXME: consider making the function pointer case produce
+ // strong_ordering not strong_equality, per P0946R0-Jax18 discussion
+ // and direction polls
+ return CCT::StrongEquality;
+ // C++2a [expr.spaceship]p8: If the composite pointer type is an object
+ // pointer type, p <=> q is of type std::strong_ordering.
+ if (Ty->isPointerType()) {
+ // P0946R0: Comparisons between a null pointer constant and an object
+ // pointer result in std::strong_equality
+ if (IsMixedNullCompare)
+ return ComparisonCategoryType::StrongEquality;
+ return CCT::StrongOrdering;
+ }
+ return None;
+}
+
+// FIXME(EricWF): The rules to deduce the composite argument type are actually
+// quite complicated. However the rules to compute the comparison type for a
+// single builtin argument type are simple, and so are the rules to compute the
+// common comparison type. So we do that to determine the "likely" comparison
+// category type for the specified set of parameter types for a builtin
+// function.
+//
+// This information is used during overload resolution to determine if the
+// rewritten expression '(LHS <=> RHS) @ 0' (or the reversed candidate) is
+// valid for a given '@'. For example `(MemPtr <=> MemPtr) < 0` is ill-formed
+// because `std::strong_equality` cannot be used in a relational operator.
+Optional<ComparisonCategoryType>
+ComparisonCategories::computeComparisonTypeForBuiltin(QualType LHSTy,
+ QualType RHSTy) {
+ QualType Args[2] = {LHSTy, RHSTy};
+ SmallVector<ComparisonCategoryType, 8> TypeKinds;
+ for (auto QT : Args) {
+ Optional<ComparisonCategoryType> CompType =
+ computeComparisonTypeForBuiltin(QT);
+ if (!CompType)
+ return None;
+ TypeKinds.push_back(*CompType);
+ }
+ return computeCommonComparisonType(TypeKinds);
+}
+
+bool ComparisonCategoryInfo::isUsableWithOperator(
+ ComparisonCategoryType CompKind, BinaryOperatorKind Opcode) {
+ assert(BinaryOperator::isComparisonOp(Opcode));
+ if (BinaryOperator::isRelationalOp(Opcode))
+ return isOrdered(CompKind);
+ // We either have an equality or three-way opcode. These are all OK for
+ // any comparison category type.
+ return true;
+}
+
+/// C++2a [class.spaceship]p4 - compute the common category type.
+const ComparisonCategoryInfo *ComparisonCategories::computeCommonComparisonType(
+ ArrayRef<QualType> Types) const {
+ SmallVector<ComparisonCategoryType, 8> Kinds;
+ // If any type is not a comparison category, return nullptr.
+ for (auto Ty : Types) {
+ const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
+ // --- If any T is not a comparison category type, U is void.
+ if (!Info)
+ return nullptr;
+ Kinds.push_back(Info->Kind);
+ }
+ Optional<ComparisonCategoryType> CommonType =
+ computeCommonComparisonType(Kinds);
+ if (!CommonType)
+ return nullptr;
+ return lookupInfo(*CommonType);
+}
+
+Optional<ComparisonCategoryType>
+ComparisonCategories::computeCommonComparisonType(
+ ArrayRef<ComparisonCategoryType> Types) {
+ using CCT = ComparisonCategoryType;
+ std::array<unsigned, static_cast<unsigned>(CCT::Last) + 1> Seen = {};
+ auto Count = [&](CCT T) { return Seen[static_cast<unsigned>(T)]; };
+
+ // Count the number of times each comparison category type occurs in the
+ // specified type list.
+ for (auto TyKind : Types)
+ Seen[static_cast<unsigned>(TyKind)]++;
+
+ // --- Otherwise, if at least one Ti is std::weak_equality, or at least one
+ // Ti is std::strong_equality and at least one Tj is
+ // std::partial_ordering or std::weak_ordering, U is
+ // std::weak_equality.
+ if (Count(CCT::WeakEquality) ||
+ (Count(CCT::StrongEquality) &&
+ (Count(CCT::PartialOrdering) || Count(CCT::WeakOrdering))))
+ return CCT::WeakEquality;
+
+ // --- Otherwise, if at least one Ti is std::strong_equality, U is
+ // std::strong_equality
+ if (Count(CCT::StrongEquality))
+ return CCT::StrongEquality;
+
+ // --- Otherwise, if at least one Ti is std::partial_ordering, U is
+ // std::partial_ordering.
+ if (Count(CCT::PartialOrdering))
+ return CCT::PartialOrdering;
+
+ // --- Otherwise, if at least one Ti is std::weak_ordering, U is
+ // std::weak_ordering.
+ if (Count(CCT::WeakOrdering))
+ return CCT::WeakOrdering;
+
+ // --- Otherwise, U is std::strong_ordering.
+ return CCT::StrongOrdering;
+}
Index: include/clang/Serialization/ASTBitCodes.h
===================================================================
--- include/clang/Serialization/ASTBitCodes.h
+++ include/clang/Serialization/ASTBitCodes.h
@@ -1811,6 +1811,7 @@
EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr
EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
EXPR_CXX_FOLD, // CXXFoldExpr
+ EXPR_CXX_REWRITTEN_OPERATOR, // CXXRewrittenOperatorExpr
// CUDA
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -43,6 +43,7 @@
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/IdentifierResolver.h"
#include "clang/Sema/ObjCMethodList.h"
+#include "clang/Sema/Overload.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/TypoCorrection.h"
@@ -2784,11 +2785,18 @@
TemplateArgumentListInfo *ExplicitTemplateArgs,
OverloadCandidateSet& CandidateSet,
bool PartialOverloading = false);
-
+ void AddRewrittenOperatorCandidates(OverloadedOperatorKind Op,
+ SourceLocation OpLoc,
+ ArrayRef<Expr *> InputArgs,
+ const UnresolvedSetImpl &Fns,
+ OverloadCandidateSet &CandidateSet,
+ bool PerformADL);
// Emit as a 'note' the specific overload candidate
void NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
QualType DestType = QualType(),
- bool TakingAddress = false);
+ bool TakingAddress = false,
+ RewrittenOverloadCandidateKind ROC = ROC_None);
+ void NoteOverloadCandidate(OverloadCandidate *Ovl);
// Emit as a series of 'note's all template and non-templates identified by
// the expression Expr
@@ -2920,16 +2928,21 @@
const UnresolvedSetImpl &Fns,
Expr *input, bool RequiresADL = true);
- ExprResult CreateOverloadedBinOp(SourceLocation OpLoc,
- BinaryOperatorKind Opc,
- const UnresolvedSetImpl &Fns,
- Expr *LHS, Expr *RHS,
- bool RequiresADL = true);
+ ExprResult CreateOverloadedBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc,
+ const UnresolvedSetImpl &Fns, Expr *LHS,
+ Expr *RHS, bool RequiresADL = true,
+ bool AllowRewrittenCandidates = true);
ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
SourceLocation RLoc,
Expr *Base,Expr *Idx);
+ ExprResult BuildBinaryOperatorCandidate(SourceLocation OpLoc,
+ BinaryOperatorKind Opc,
+ const OverloadCandidate &Ovl,
+ Expr *LHS, Expr *RHS,
+ bool HadMultipleCandidates);
+
ExprResult
BuildCallToMemberFunction(Scope *S, Expr *MemExpr,
SourceLocation LParenLoc,
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h
+++ include/clang/Sema/Overload.h
@@ -719,6 +719,10 @@
/// This candidate was not viable because it is a non-default multiversioned
/// function.
ovl_non_default_multiversion_function,
+
+ // Thes candidate was not viable because the return type of the rewritten
+ // expression was not a valid operand to the original binary operator.
+ ovl_rewritten_operand_non_valid_for_operator
};
/// A list of implicit conversion sequences for the arguments of an
@@ -755,21 +759,25 @@
ConversionFixItGenerator Fix;
/// Viable - True to indicate that this overload candidate is viable.
- bool Viable;
+ bool Viable : 1;
/// IsSurrogate - True to indicate that this candidate is a
/// surrogate for a conversion to a function pointer or reference
/// (C++ [over.call.object]).
- bool IsSurrogate;
+ bool IsSurrogate : 1;
/// IgnoreObjectArgument - True to indicate that the first
/// argument's conversion, which for this function represents the
/// implicit object argument, should be ignored. This will be true
/// when the candidate is a static member function (where the
/// implicit object argument is just a placeholder) or a
/// non-static member function when the call doesn't have an
/// object argument.
- bool IgnoreObjectArgument;
+ bool IgnoreObjectArgument : 1;
+
+ /// RewrittenKind - For rewritten operator candidates, the kind of rewritten
+ /// candidate it is: rewritten or synthesized.
+ unsigned char RewrittenOpKind : 2;
/// FailureKind - The reason why this candidate is not viable.
/// Actually an OverloadFailureKind.
@@ -812,6 +820,24 @@
return CanFix;
}
+ /// \brief Return the index of the conversion corresponding to the specified
+ /// argument index. If this is not a synthesized candidate, 'Idx' is
+ /// returned. Otherwise the index corresponding to the reversed parameter
+ /// is returned.
+ unsigned getConversionIndexForArgIndex(unsigned Idx) const;
+
+ /// \brief Return the conversion sequence for the specified argument index.
+ /// If this is a synthesized candidate, the argument index is reversed.
+ const ImplicitConversionSequence &getConversion(unsigned ArgIdx) const;
+
+ /// \brief Returns the parameter type corresponding to the specified index.
+ /// (The index is not reversed for synthesized candidates).
+ QualType getParamType(unsigned Idx) const {
+ if (Function)
+ return Function->getParamDecl(Idx)->getType();
+ return BuiltinParamTypes[Idx];
+ }
+
unsigned getNumParams() const {
if (IsSurrogate) {
auto STy = Surrogate->getConversionType();
@@ -823,6 +849,10 @@
return Function->getNumParams();
return ExplicitCallArguments;
}
+
+ RewrittenOverloadCandidateKind getRewrittenKind() const {
+ return static_cast<RewrittenOverloadCandidateKind>(RewrittenOpKind);
+ }
};
/// OverloadCandidateSet - A set of overload candidates, used in C++
@@ -853,15 +883,22 @@
private:
SmallVector<OverloadCandidate, 16> Candidates;
- llvm::SmallPtrSet<Decl *, 16> Functions;
+ using DeclKindPair =
+ llvm::PointerIntPair<Decl *, 2, RewrittenOverloadCandidateKind>;
+ using DeclKindSet = llvm::SmallSet<DeclKindPair, 4>;
+ DeclKindSet Functions;
+
+ private:
// Allocator for ConversionSequenceLists. We store the first few of these
// inline to avoid allocation for small sets.
llvm::BumpPtrAllocator SlabAllocator;
SourceLocation Loc;
CandidateSetKind Kind;
+ RewrittenOverloadCandidateKind AddingOverloadKind;
+
constexpr static unsigned NumInlineBytes =
24 * sizeof(ImplicitConversionSequence);
unsigned NumInlineBytesUsed = 0;
@@ -896,19 +933,40 @@
void destroyCandidates();
public:
+ friend struct AddingRewrittenCandidateGuard;
+ struct AddingRewrittenCandidateGuard {
+ AddingRewrittenCandidateGuard(
+ OverloadCandidateSet &Candidates,
+ RewrittenOverloadCandidateKind NewOverloadKind)
+ : Candidates(Candidates) {
+ OldRewrittenKind = Candidates.AddingOverloadKind;
+ Candidates.AddingOverloadKind = NewOverloadKind;
+ }
+ ~AddingRewrittenCandidateGuard() {
+ Candidates.AddingOverloadKind = OldRewrittenKind;
+ }
+ OverloadCandidateSet &Candidates;
+ RewrittenOverloadCandidateKind OldRewrittenKind;
+ };
+
+ public:
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
- : Loc(Loc), Kind(CSK) {}
+ : Loc(Loc), Kind(CSK), AddingOverloadKind(ROC_None) {}
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
~OverloadCandidateSet() { destroyCandidates(); }
SourceLocation getLocation() const { return Loc; }
CandidateSetKind getKind() const { return Kind; }
+ RewrittenOverloadCandidateKind getRewrittenKind() const {
+ return AddingOverloadKind;
+ }
/// Determine when this overload candidate will be new to the
/// overload set.
bool isNewCandidate(Decl *F) {
- return Functions.insert(F->getCanonicalDecl()).second;
+ DeclKindPair P(F->getCanonicalDecl(), AddingOverloadKind);
+ return Functions.insert(P).second;
}
/// Clear out all of the candidates.
@@ -948,6 +1006,7 @@
C.Conversions = Conversions.empty()
? allocateConversionSequences(NumConversions)
: Conversions;
+ C.RewrittenOpKind = AddingOverloadKind;
return C;
}
Index: include/clang/Basic/StmtNodes.td
===================================================================
--- include/clang/Basic/StmtNodes.td
+++ include/clang/Basic/StmtNodes.td
@@ -146,6 +146,7 @@
def MaterializeTemporaryExpr : DStmt<Expr>;
def LambdaExpr : DStmt<Expr>;
def CXXFoldExpr : DStmt<Expr>;
+def CXXRewrittenOperatorExpr : DStmt<Expr>;
// C++ Coroutines TS expressions
def CoroutineSuspendExpr : DStmt<Expr, 1>;
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -3534,7 +3534,11 @@
"is the implicit copy assignment operator|"
"is the implicit move assignment operator|"
"inherited constructor|"
- "inherited constructor }0%2"
+ "inherited constructor |"
+ "rewritten comparison operator|"
+ "synthesized comparison operator|"
+ "rewritten comparison operator |"
+ "synthesized comparison operator }0%2"
"%select{| has different class%diff{ (expected $ but has $)|}4,5"
"| has different number of parameters (expected %4 but has %5)"
"| has type mismatch at %ordinal4 parameter"
@@ -3622,7 +3626,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor}0 %select{|template }1"
+ "inherited constructor|"
+ "rewritten comparison operator|sythesized comparison operator|"
+ "rewritten comparison operator|synthesized comparison operator}0 %select{|template }1"
"not viable: requires%select{ at least| at most|}2 %3 argument%s3, but %4 "
"%plural{1:was|:were}4 provided">;
@@ -3634,7 +3640,10 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor}0 %select{|template }1not viable: "
+ "inherited constructor|"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator|sythesized comparison operator}0 "
+ "%select{|template }1not viable: "
"%select{requires at least|allows at most single|requires single}2 "
"argument %3, but %plural{0:no|:%4}4 arguments were provided">;
@@ -3647,7 +3656,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 has been "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1 has been "
"%select{explicitly made unavailable|explicitly deleted|"
"implicitly deleted}2">;
@@ -3665,7 +3676,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1 "
"not viable: cannot convert argument of incomplete type "
"%diff{$ to $|to parameter type}2,3 for "
"%select{%ordinal5 argument|object argument}4"
@@ -3682,7 +3695,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1 "
"not viable: cannot convert initializer list argument to %3">;
def note_ovl_candidate_bad_overload : Note<"candidate "
"%select{function|function|constructor|"
@@ -3693,7 +3708,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1"
+ "inherited constructor "
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1"
" not viable: no overload of %3 matching %2 for %ordinal4 argument">;
def note_ovl_candidate_bad_conv : Note<"candidate "
"%select{function|function|constructor|"
@@ -3704,7 +3721,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1"
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1"
" not viable: no known conversion "
"%diff{from $ to $|from argument type to parameter type}2,3 for "
"%select{%ordinal5 argument|object argument}4"
@@ -3721,7 +3740,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1"
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1"
" not viable: cannot implicitly convert argument "
"%diff{of type $ to $|type to parameter type}2,3 for "
"%select{%ordinal5 argument|object argument}4 under ARC">;
@@ -3734,7 +3755,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1"
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1"
" not viable: expects an l-value for "
"%select{%ordinal3 argument|object argument}2">;
def note_ovl_candidate_bad_addrspace : Note<"candidate "
@@ -3746,8 +3769,10 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 not viable: "
- "%select{%ordinal6|'this'}5 argument (%2) is in "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1"
+ " not viable: %select{%ordinal6|'this'}5 argument (%2) is in "
"address space %3, but parameter must be in address space %4">;
def note_ovl_candidate_bad_gc : Note<"candidate "
"%select{function|function|constructor|"
@@ -3758,7 +3783,8 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 not viable: "
+ "inherited constructor }0%1"
+ " not viable: "
"%select{%ordinal6|'this'}5 argument (%2) has %select{no|__weak|__strong}3 "
"ownership, but parameter has %select{no|__weak|__strong}4 ownership">;
def note_ovl_candidate_bad_ownership : Note<"candidate "
@@ -3770,7 +3796,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 not viable: "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
"%select{%ordinal6|'this'}5 argument (%2) has "
"%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}3 ownership,"
" but parameter has %select{no|__unsafe_unretained|__strong|__weak|"
@@ -3791,7 +3819,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 not viable: "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
"%ordinal4 argument (%2) would lose "
"%select{const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}3 qualifier"
@@ -3805,7 +3835,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 not viable: "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
"%ordinal4 argument (%2) would lose __unaligned qualifier">;
def note_ovl_candidate_bad_base_to_derived_conv : Note<"candidate "
"%select{function|function|constructor|"
@@ -3816,7 +3848,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor }0%1 not viable: "
+ "inherited constructor |"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
"cannot %select{convert from|convert from|bind}2 "
"%select{base class pointer|superclass|base class object of type}2 %3 to "
"%select{derived class pointer|subclass|derived class reference}2 %4 for "
@@ -3830,7 +3864,9 @@
"function (the implicit copy assignment operator)|"
"function (the implicit move assignment operator)|"
"inherited constructor|"
- "inherited constructor}0 not viable: "
+ "inherited constructor|"
+ "rewritten comparison operator|synthesized comparison operator|"
+ "rewritten comparison operator|synthesized comparison operator}0 not viable: "
"call to "
"%select{__device__|__global__|__host__|__host__ __device__|invalid}1 function from"
" %select{__device__|__global__|__host__|__host__ __device__|invalid}2 function">;
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -247,6 +247,14 @@
unsigned IsUnique : 1;
};
+ class CXXRewrittenOperatorExprBitfields {
+ friend class CXXRewrittenOperatorExpr;
+
+ unsigned : NumExprBits;
+
+ unsigned Kind : 2;
+ };
+
class ObjCIndirectCopyRestoreExprBitfields {
friend class ObjCIndirectCopyRestoreExpr;
@@ -309,6 +317,7 @@
InitListExprBitfields InitListExprBits;
TypeTraitExprBitfields TypeTraitExprBits;
CoawaitExprBitfields CoawaitBits;
+ CXXRewrittenOperatorExprBitfields CXXRewrittenOperatorBits;
};
public:
Index: include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -2548,6 +2548,7 @@
DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {})
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
DEF_TRAVERSE_STMT(AtomicExpr, {})
+DEF_TRAVERSE_STMT(CXXRewrittenOperatorExpr, {})
// For coroutines expressions, traverse either the operand
// as written or the implied calls, depending on what the
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_AST_EXPRCXX_H
#define LLVM_CLANG_AST_EXPRCXX_H
+#include "clang/AST/ComparisonCategories.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
@@ -87,6 +88,8 @@
SourceRange getSourceRangeImpl() const LLVM_READONLY;
public:
+ friend class ASTReader;
+ friend class ASTWriter;
friend class ASTStmtReader;
friend class ASTStmtWriter;
@@ -4207,6 +4210,65 @@
child_range children() { return child_range(SubExprs, SubExprs + 2); }
};
+// FIXME(EricWF): Document this
+class CXXRewrittenOperatorExpr : public Expr {
+public:
+ using RewrittenOpKind = RewrittenOverloadCandidateKind;
+ typedef BinaryOperatorKind Opcode;
+
+private:
+ friend class ASTReader;
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
+
+ Stmt *Rewritten;
+
+ CXXRewrittenOperatorExpr(EmptyShell Empty)
+ : Expr(CXXRewrittenOperatorExprClass, Empty) {}
+
+public:
+ CXXRewrittenOperatorExpr(RewrittenOpKind Kind, Expr *Rewritten)
+ : Expr(CXXRewrittenOperatorExprClass, Rewritten->getType(),
+ Rewritten->getValueKind(), Rewritten->getObjectKind(),
+ /*Dependent*/ Rewritten->isTypeDependent(),
+ Rewritten->isValueDependent(),
+ Rewritten->isInstantiationDependent(),
+ Rewritten->containsUnexpandedParameterPack()),
+ Rewritten(Rewritten) {
+ CXXRewrittenOperatorBits.Kind = Kind;
+ }
+
+ RewrittenOpKind getRewrittenKind() const {
+ return static_cast<RewrittenOpKind>(CXXRewrittenOperatorBits.Kind);
+ }
+ void setRewrittenKind(RewrittenOpKind Kind) {
+ CXXRewrittenOperatorBits.Kind = Kind;
+ }
+
+ Expr *getRewrittenExpr() const { return static_cast<Expr *>(Rewritten); }
+ Opcode getOpcode() const;
+
+ bool isRewrittenAsReversed() const {
+ return getRewrittenKind() == ROC_AsReversedThreeWay;
+ }
+ bool isRewrittenAsThreeWay() const { return getOriginalOpcode() != BO_Cmp; }
+
+ SourceLocation getLocStart() const { return Rewritten->getLocStart(); }
+ SourceLocation getLocEnd() const { return Rewritten->getLocEnd(); }
+ SourceLocation getExprLoc() const { return getRewrittenExpr()->getExprLoc(); }
+ SourceLocation getOperatorLoc() const;
+
+ Opcode getOriginalOpcode() const;
+ Expr *getOriginalLHS() const;
+ Expr *getOriginalRHS() const;
+
+ child_range children() { return child_range(&Rewritten, &Rewritten + 1); }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXRewrittenOperatorExprClass;
+ }
+};
+
/// Represents an expression that might suspend coroutine execution;
/// either a co_await or co_yield expression.
///
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h
+++ include/clang/AST/Expr.h
@@ -886,6 +886,9 @@
setIsUnique(false);
}
+ /// Create an OpaqueValueExpr representing the specified source expression
+ static OpaqueValueExpr *Create(const ASTContext &Ctx, Expr *Source);
+
/// Given an expression which invokes a copy constructor --- i.e. a
/// CXXConstructExpr, possibly wrapped in an ExprWithCleanups ---
/// find the OpaqueValueExpr that's the source of the construction.
Index: include/clang/AST/ComparisonCategories.h
===================================================================
--- include/clang/AST/ComparisonCategories.h
+++ include/clang/AST/ComparisonCategories.h
@@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H
#define LLVM_CLANG_AST_COMPARISONCATEGORIES_H
+#include "clang/AST/OperationKinds.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/DenseMap.h"
@@ -65,6 +66,17 @@
Last = Unordered
};
+/// OperatorOverloadCandidateKind - The kind of the operator candidate in
+/// accordance with [over.match.oper].
+enum RewrittenOverloadCandidateKind : unsigned char {
+ /// Not a rewritten candidate.
+ ROC_None,
+ /// Rewritten but not synthesized.
+ ROC_AsThreeWay,
+ /// Both rewritten and synthesized.
+ ROC_AsReversedThreeWay
+};
+
class ComparisonCategoryInfo {
friend class ComparisonCategories;
friend class Sema;
@@ -127,28 +139,42 @@
}
/// True iff the comparison category is an equality comparison.
- bool isEquality() const { return !isOrdered(); }
+ bool isEquality() const { return isEquality(Kind); }
+ static bool isEquality(ComparisonCategoryType Kind) {
+ return !isOrdered(Kind);
+ }
/// True iff the comparison category is a relational comparison.
- bool isOrdered() const {
+ bool isOrdered() const { return isOrdered(Kind); }
+ static bool isOrdered(ComparisonCategoryType Kind) {
using CCK = ComparisonCategoryType;
return Kind == CCK::PartialOrdering || Kind == CCK::WeakOrdering ||
Kind == CCK::StrongOrdering;
}
/// True iff the comparison is "strong". i.e. it checks equality and
/// not equivalence.
- bool isStrong() const {
+ bool isStrong() const { return isStrong(Kind); }
+ static bool isStrong(ComparisonCategoryType Kind) {
using CCK = ComparisonCategoryType;
return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering;
}
/// True iff the comparison is not totally ordered.
- bool isPartial() const {
+ bool isPartial() const { return isPartial(Kind); }
+ static bool isPartial(ComparisonCategoryType Kind) {
using CCK = ComparisonCategoryType;
return Kind == CCK::PartialOrdering;
}
+ /// Return whether the specified comparison category type can be used as
+ /// an operand to the specified relational binary operator.
+ static bool isUsableWithOperator(ComparisonCategoryType CompKind,
+ BinaryOperatorKind Opcode);
+ bool isUsableWithOperator(BinaryOperatorKind Opc) const {
+ return isUsableWithOperator(Kind, Opc);
+ }
+
/// Converts the specified result kind into the the correct result kind
/// for this category. Specifically it lowers strong equality results to
/// weak equivalence if needed.
@@ -189,6 +215,28 @@
static StringRef getCategoryString(ComparisonCategoryType Kind);
static StringRef getResultString(ComparisonCategoryResult Kind);
+ /// Return the comparison category information for the
+ /// "common comparison type" for a specified list of types. If there is no
+ /// such common comparison type, or if any of the specified types are not
+ /// comparison category types, null is returned.
+ const ComparisonCategoryInfo *
+ computeCommonComparisonType(ArrayRef<QualType> Types) const;
+ static Optional<ComparisonCategoryType>
+ computeCommonComparisonType(ArrayRef<ComparisonCategoryType> Types);
+
+ /// Return the comparison category type which would be returned
+ /// for a builtin comparison operator taking the specified type, or None if no
+ /// such type exists.
+ ///
+ /// \param Ty The composite comparison type
+ /// \param IsMixedNullCompare True if exactly one of the operands is a null
+ /// pointer constant.
+ static Optional<ComparisonCategoryType>
+ computeComparisonTypeForBuiltin(QualType Ty, bool IsMixedNullCompare = false);
+
+ static Optional<ComparisonCategoryType>
+ computeComparisonTypeForBuiltin(QualType LHSTy, QualType RHSTy);
+
/// Return the list of results which are valid for the specified
/// comparison category type.
static std::vector<ComparisonCategoryResult>
@@ -209,6 +257,7 @@
/// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is
/// possible.
const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;
+ const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
public:
/// Return the cached comparison category information for the
@@ -223,9 +272,6 @@
}
private:
- const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
-
-private:
friend class ASTContext;
explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits