EricWF updated this revision to Diff 145390.
EricWF marked 7 inline comments as done.
EricWF added a comment.
- Remove the `= default` changes as requested.
- Attempt to work around ambiguity in overload resolution caused by differing
lvalue or qualification conversions when ranking synthesized candidates. For
example:
struct T {};
auto operator<=>(T const&, T&&);
T{} <=> T{}; // Ambiguous according to the current spec.
The workaround ignores worse implicit conversions when the conversions are both
an exact match and a rewritten candidate tie-breaker is present.
https://reviews.llvm.org/D45680
Files:
include/clang/AST/ExprCXX.h
include/clang/AST/RecursiveASTVisitor.h
include/clang/Basic/StmtNodes.td
include/clang/Sema/Overload.h
include/clang/Sema/Sema.h
include/clang/Serialization/ASTBitCodes.h
lib/AST/Expr.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
@@ -293,20 +293,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() {
@@ -422,3 +423,125 @@
}
} // 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 {};
+// expected-note@+1 2 {{candidate}}
+Tag<0> operator<=>(X, Y) {
+ return {};
+}
+// expected-note@+1 2 {{candidate}}
+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;
+// expected-note@+2 {{candidate}}
+struct T {
+ std::strong_ordering operator<=>(U const &RHS) const;
+};
+// expected-note@+2 {{candidate}}
+struct U {
+ std::strong_ordering operator<=>(T const &RHS) const;
+};
+
+void test() {
+ // expected-error@+1 {{use of overloaded operator '<' is ambiguous}}
+ (void)(T{} < U{});
+}
+
+} // namespace TestOvlMatchingIgnoresImplicitObject
+
+namespace TestImplicitConversionAmbiguity {
+struct T {
+ int x;
+};
+auto operator<=>(T const &LHS, T &&RHS) {
+ return LHS.x <=> RHS.x;
+}
+auto operator<(T const &, T &&) {
+ return std::strong_equality::equal;
+}
+void test() {
+ {
+ // Chooses non-rewritten operator<
+ auto R = T{} < T{};
+ ASSERT_EXPR_TYPE(R, std::strong_equality);
+ }
+ { // Chooses non-rewritten operator<
+ T const t{};
+ auto R = t < T{};
+ ASSERT_EXPR_TYPE(R, std::strong_equality);
+ }
+ { // Chooses synthesized <=>
+ T const t{};
+ auto R = T{} < t;
+ ASSERT_EXPR_TYPE(R, bool);
+ }
+ { // Chooses non-rewritten <=>
+ auto R = T{} <=> T{};
+ ASSERT_EXPR_TYPE(R, std::strong_ordering);
+ }
+ { // Chooses non-rewritten operator<=>
+ T const t{};
+ auto R = t <=> T{};
+ ASSERT_EXPR_TYPE(R, std::strong_ordering);
+ }
+ { // Chooses synthesized <=>
+ T const t{};
+ auto R = T{} <=> t;
+ ASSERT_EXPR_TYPE(R, std::strong_ordering);
+ }
+}
+} // namespace TestImplicitConversionAmbiguity
Index: test/CodeGenCXX/cxx2a-compare.cpp
===================================================================
--- test/CodeGenCXX/cxx2a-compare.cpp
+++ test/CodeGenCXX/cxx2a-compare.cpp
@@ -184,3 +184,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:
@@ -2897,43 +2898,6 @@
const ProgramPointTag *tag,
QualType LoadTy) {
assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc.");
-
- // Are we loading from a region? This actually results in two loads; one
- // to fetch the address of the referenced value and one to fetch the
- // referenced value.
- if (const auto *TR =
- dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) {
-
- QualType ValTy = TR->getValueType();
- if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) {
- static SimpleProgramPointTag
- loadReferenceTag(TagProviderName, "Load Reference");
- ExplodedNodeSet Tmp;
- evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state,
- location, &loadReferenceTag,
- getContext().getPointerType(RT->getPointeeType()));
-
- // Perform the load from the referenced value.
- for (const auto I : Tmp) {
- state = I->getState();
- location = state->getSVal(BoundEx, I->getLocationContext());
- evalLoadCommon(Dst, NodeEx, BoundEx, I, state, location, tag, LoadTy);
- }
- return;
- }
- }
-
- evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy);
-}
-
-void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst,
- const Expr *NodeEx,
- const Expr *BoundEx,
- ExplodedNode *Pred,
- ProgramStateRef state,
- SVal location,
- const ProgramPointTag *tag,
- QualType LoadTy) {
assert(NodeEx);
assert(BoundEx);
// Evaluate the location (checks for bad dereferences).
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -1695,6 +1695,14 @@
Code = serialization::EXPR_CXX_FOLD;
}
+void ASTStmtWriter::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) {
+ VisitExpr(E);
+ Record.push_back(E->Kind);
+ Record.AddStmt(E->SubExprs[0]);
+ Record.AddStmt(E->SubExprs[1]);
+ 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->Kind = (CXXRewrittenOperatorExpr::RewrittenOperatorKind)Record.readInt();
+ E->SubExprs[0] = Record.readSubExpr();
+ E->SubExprs[1] = 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,13 @@
return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator);
}
+ ExprResult RebuildCXXRewrittenOperatorExpr(
+ CXXRewrittenOperatorExpr::RewrittenOperatorKind Kind, Expr *Underlying,
+ Expr *Rewritten) {
+ return new (SemaRef.Context)
+ CXXRewrittenOperatorExpr(Kind, Underlying, Rewritten);
+ }
+
/// \brief Build a new atomic operation expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -11516,6 +11523,28 @@
}
template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXRewrittenOperatorExpr(
+ CXXRewrittenOperatorExpr *E) {
+ ExprResult Orig = getDerived().TransformExpr(E->getUnderlyingExpr());
+ if (Orig.isInvalid())
+ return ExprError();
+
+ // 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 Rewritten = getDerived().TransformExpr(E->getRewrittenExpr());
+ if (Rewritten.isInvalid())
+ return ExprError();
+
+ if (getDerived().AlwaysRebuild() || Orig.get() != E->getUnderlyingExpr() ||
+ Rewritten.get() != E->getRewrittenExpr()) {
+ return getDerived().RebuildCXXRewrittenOperatorExpr(
+ E->getKind(), Orig.get(), Rewritten.get());
+ }
+ return E;
+}
+
+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_Synthesized)
+ 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;
@@ -5964,6 +5979,7 @@
Candidate.Function = Function;
Candidate.Viable = true;
Candidate.IsSurrogate = false;
+ Candidate.RewrittenOpKind = ROC_None;
Candidate.IgnoreObjectArgument = false;
Candidate.ExplicitCallArguments = Args.size();
@@ -6654,6 +6670,7 @@
Candidate.Function = MethodTmpl->getTemplatedDecl();
Candidate.Viable = false;
Candidate.IsSurrogate = false;
+ Candidate.RewrittenOpKind = ROC_None;
Candidate.IgnoreObjectArgument =
cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
ObjectType.isNull();
@@ -6718,6 +6735,7 @@
Candidate.Function = FunctionTemplate->getTemplatedDecl();
Candidate.Viable = false;
Candidate.IsSurrogate = false;
+ Candidate.RewrittenOpKind = ROC_None;
// Ignore the object argument if there is one, since we don't have an object
// type.
Candidate.IgnoreObjectArgument =
@@ -6889,6 +6907,7 @@
Candidate.FoundDecl = FoundDecl;
Candidate.Function = Conversion;
Candidate.IsSurrogate = false;
+ Candidate.RewrittenOpKind = ROC_None;
Candidate.IgnoreObjectArgument = false;
Candidate.FinalConversion.setAsIdentityConversion();
Candidate.FinalConversion.setFromType(ConvType);
@@ -7050,6 +7069,7 @@
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_deduction;
Candidate.IsSurrogate = false;
+ Candidate.RewrittenOpKind = ROC_None;
Candidate.IgnoreObjectArgument = false;
Candidate.ExplicitCallArguments = 1;
Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result,
@@ -7090,6 +7110,7 @@
Candidate.Surrogate = Conversion;
Candidate.Viable = true;
Candidate.IsSurrogate = true;
+ Candidate.RewrittenOpKind = ROC_None;
Candidate.IgnoreObjectArgument = false;
Candidate.ExplicitCallArguments = Args.size();
@@ -7247,6 +7268,7 @@
Candidate.FoundDecl = DeclAccessPair::make(nullptr, AS_none);
Candidate.Function = nullptr;
Candidate.IsSurrogate = false;
+ Candidate.RewrittenOpKind = ROC_None;
Candidate.IgnoreObjectArgument = false;
std::copy(ParamTys, ParamTys + Args.size(), Candidate.BuiltinParamTypes);
@@ -8862,20 +8884,78 @@
}
}
+/// 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 IsRelationalOrEquality =
+ BinaryOperator::isRelationalOp(Opc) || BinaryOperator::isEqualityOp(Opc);
+ 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::RewrittenCandidateContextGuard Guard(CandidateSet);
+
+ 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;
+ Ovl.RewrittenOpKind = Kind;
+ }
+ };
+
+ // If we have a relational or equality operation, add the rewritten candidates
+ // of the form: (LHS <=> RHS) @ 0
+ if (IsRelationalOrEquality)
+ AddCandidates(InputArgs, ROC_Rewritten);
+
+ // 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_Synthesized);
+}
+
/// \brief 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
@@ -8968,6 +9048,52 @@
return Cand1I == Cand1Attrs.end() ? Comparison::Equal : Comparison::Better;
}
+static Optional<bool>
+isBetterRewrittenCandidate(Sema &S, const OverloadCandidate &Cand1,
+ const OverloadCandidate &Cand2) {
+ // --- F2 is a rewritten candidate ([over.match.oper]) and F1 is not.
+ RewrittenOverloadCandidateKind C1Roc = Cand1.getRewrittenKind();
+ RewrittenOverloadCandidateKind C2Roc = Cand2.getRewrittenKind();
+ if (!C1Roc && !C2Roc)
+ return None;
+
+ 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_Synthesized || C2Roc == ROC_Synthesized) &&
+ 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());
+ if (Ovl.getRewrittenKind() == ROC_Synthesized)
+ llvm::reverse(Types);
+ return Types;
+ };
+ if (GetParamTypes(Cand1) == GetParamTypes(Cand2))
+ return C2Roc == ROC_Synthesized;
+ }
+
+ return None;
+}
+
/// isBetterOverloadCandidate - Determines whether the first overload
/// candidate is a better candidate than the second (C++ 13.3.3p1).
bool clang::isBetterOverloadCandidate(
@@ -9008,8 +9134,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;
@@ -9020,23 +9146,54 @@
if (HasBetterConversion)
return true;
+ // Evaluate the C++2a tie-breakers for rewritten expressions early. The result
+ // is needed when comparing conversion sequences if either candidate is
+ // rewritten.
+ Optional<bool> RewrittenCandidateTieBreaker =
+ isBetterRewrittenCandidate(S, Cand1, Cand2);
+
// C++ [over.match.best]p1:
// A viable function F1 is defined to be a better function than another
// viable function F2 if for all arguments i, ICSi(F1) is not a worse
// conversion sequence than ICSi(F2), and then...
for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
- switch (CompareImplicitConversionSequences(S, Loc,
- Cand1.Conversions[ArgIdx],
- Cand2.Conversions[ArgIdx])) {
+ const ImplicitConversionSequence &ICS1 = Cand1.getConversion(ArgIdx);
+ const ImplicitConversionSequence &ICS2 = Cand2.getConversion(ArgIdx);
+ ImplicitConversionSequence::CompareKind CompareRes =
+ CompareImplicitConversionSequences(S, Loc, ICS1, ICS2);
+
+ // FIXME(EricWF): This is a workaround for a bug in the current
+ // specification which often causes rewritten or sythesized candidates to be
+ // ambiguuous. For example:
+ // auto operator<=>(T const&, T&&);
+ // T{} <=> T{};
+ // auto operator<(T const&, T&&);
+ // T{} < T{};
+ //
+ // The current workaround is to ignore worse implicit conversions
+ // sequences which rank as an exact match when the first candidate is better
+ // than the second according to the rewritten candidate tie-breakers
+ // specified in C++2a [over.match.oper]p1.10.
+ if (RewrittenCandidateTieBreaker && ICS1.isStandard() &&
+ ICS2.isStandard() && ICS1.Standard.getRank() == ICR_Exact_Match &&
+ ICS2.Standard.getRank() == ICR_Exact_Match) {
+ // If the lvalue or qualified conversions were worse but the candidate
+ // is better according to the type-breakers for C++2a rewritten
+ // expressions, then we ignore the differences in conversion sequences.
+ if (CompareRes == ImplicitConversionSequence::Worse &&
+ RewrittenCandidateTieBreaker.getValue())
+ CompareRes = ImplicitConversionSequence::Indistinguishable;
+ }
+ switch (CompareRes) {
case ImplicitConversionSequence::Better:
// Cand1 has a better conversion sequence.
HasBetterConversion = true;
break;
- case ImplicitConversionSequence::Worse:
+ case ImplicitConversionSequence::Worse: {
// Cand1 can't be better than Cand2.
return false;
-
+ }
case ImplicitConversionSequence::Indistinguishable:
// Do nothing.
break;
@@ -9133,6 +9290,10 @@
// Inherited from sibling base classes: still ambiguous.
}
+ // Check C++2a tie-breakers for rewritten candidates
+ if (RewrittenCandidateTieBreaker)
+ return *RewrittenCandidateTieBreaker;
+
// Check C++17 tie-breakers for deduction guides.
{
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
@@ -9245,9 +9406,9 @@
/// function, \p Best points to the candidate function found.
///
/// \returns The result of overload resolution.
-OverloadingResult
-OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
- iterator &Best) {
+OverloadingResult OverloadCandidateSet::BestViableFunction(
+ Sema &S, SourceLocation Loc, iterator &Best,
+ SmallVectorImpl<OverloadCandidate *> *EquivalentCands) {
llvm::SmallVector<OverloadCandidate *, 16> Candidates;
std::transform(begin(), end(), std::back_inserter(Candidates),
[](OverloadCandidate &Cand) { return &Cand; });
@@ -9289,33 +9450,42 @@
if (Best == end())
return OR_No_Viable_Function;
- llvm::SmallVector<const NamedDecl *, 4> EquivalentCands;
-
+ llvm::SmallVector<const NamedDecl *, 4> EquivalentFunctionCands;
+ if (EquivalentCands)
+ EquivalentCands->push_back(&(*Best));
// Make sure that this function is better than every other viable
// function. If not, we have an ambiguity.
for (auto *Cand : Candidates) {
if (Cand->Viable && Cand != Best &&
!isBetterOverloadCandidate(S, *Best, *Cand, Loc, Kind)) {
+ if (EquivalentCands)
+ EquivalentCands->push_back(Cand);
if (S.isEquivalentInternalLinkageDeclaration(Best->Function,
Cand->Function)) {
- EquivalentCands.push_back(Cand->Function);
+ EquivalentFunctionCands.push_back(Cand->Function);
continue;
}
+ if (EquivalentCands)
+ continue;
Best = end();
return OR_Ambiguous;
}
}
+ if (EquivalentCands && EquivalentCands->size() > 1) {
+ Best = end();
+ return OR_Ambiguous;
+ }
// Best is the best viable function.
if (Best->Function &&
(Best->Function->isDeleted() ||
S.isFunctionConsideredUnavailable(Best->Function)))
return OR_Deleted;
- if (!EquivalentCands.empty())
+ if (!EquivalentFunctionCands.empty())
S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
- EquivalentCands);
+ EquivalentFunctionCands);
return OR_Success;
}
@@ -12228,6 +12398,313 @@
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.Conversions[0], Sema::AA_Passing);
+ if (ArgsRes0.isInvalid())
+ return ExprError();
+ Args[0] = ArgsRes0.get();
+
+ ExprResult ArgsRes1 =
+ PerformImplicitConversion(Args[1], Ovl.BuiltinParamTypes[1],
+ Ovl.Conversions[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]);
+}
+
+namespace {
+
+/// \brief RewrittenOverloadResolver - This class handles initial
+/// overload resolution for candidate sets which include rewritten candidates.
+///
+/// Rewritten candidates haven't been fully checked for validity. They may still
+/// be invalid if:
+/// (A) The rewritten candidate is a builtin, but semantic checking of the
+/// builtin would fail.
+/// (B) The result of the "partially rewritten expression"
+/// (ie the (LHS <=> RHS) part) is ill-formed when used as an operand to
+/// (<result> @ 0) or (0 @ <result>).
+///
+/// TODO: Separate out the bits of semantic checking for builtin spaceship
+/// operators which determine validity and the return type, and use that instead
+/// of building the full expression to check validity.
+class RewrittenOverloadResolver {
+ enum CheckOverloadResult { Done, Continue };
+
+public:
+ RewrittenOverloadResolver(Sema &S, SourceLocation OpLoc,
+ BinaryOperatorKind Opc, ArrayRef<Expr *> Args,
+ const UnresolvedSetImpl &Fns, bool PerformADL,
+ OverloadCandidateSet &CS)
+ : S(S), OpLoc(OpLoc), Opc(Opc), Args(Args), Fns(Fns),
+ PerformADL(PerformADL), CandidateSet(CS) {}
+
+ ExprResult ResolveRewrittenCandidates() {
+ ExprResult FinalResult = ExprError();
+ OverloadCandidateSet::iterator Best;
+ OverloadingResult OvlRes;
+ llvm::SmallVector<OverloadCandidate *, 4> EquivCands;
+ do {
+ EquivCands.clear();
+ OvlRes = CandidateSet.BestViableFunction(S, OpLoc, Best, &EquivCands);
+ } while (Continue == RemoveNonViableRewrittenCandidates(
+ OvlRes, Best, EquivCands, FinalResult));
+ return FinalResult;
+ }
+private:
+ CheckOverloadResult RemoveNonViableRewrittenCandidates(
+ OverloadingResult OvlRes, OverloadCandidateSet::iterator Best,
+ ArrayRef<OverloadCandidate *> EquivCands, ExprResult &FinalResult);
+ ExprResult BuildRewrittenCandidate(const OverloadCandidate &Ovl);
+
+ RewrittenOverloadResolver(RewrittenOverloadResolver const &) = delete;
+ RewrittenOverloadResolver &
+ operator=(RewrittenOverloadResolver const &) = delete;
+
+private:
+ Sema &S;
+ SourceLocation OpLoc;
+ BinaryOperatorKind Opc;
+ ArrayRef<Expr *> Args;
+ const UnresolvedSetImpl &Fns;
+ bool PerformADL;
+ OverloadCandidateSet &CandidateSet;
+};
+} // end namespace
+
+ExprResult RewrittenOverloadResolver::BuildRewrittenCandidate(
+ const OverloadCandidate &Ovl) {
+ Expr *RewrittenArgs[2] = {Args[0], Args[1]};
+ assert(Ovl.getRewrittenKind());
+ bool IsSynthesized = Ovl.getRewrittenKind() == ROC_Synthesized;
+ if (IsSynthesized)
+ std::swap(RewrittenArgs[0], RewrittenArgs[1]);
+
+ ExprResult RewrittenRes = ExprError();
+
+ // 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.
+ RewrittenRes = S.BuildBinaryOperatorCandidate(
+ OpLoc, BO_Cmp, Ovl, RewrittenArgs[0], RewrittenArgs[1],
+ /*HadMultipleCandidates*/ false);
+ if (RewrittenRes.isInvalid())
+ return ExprError();
+
+ // 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_Synthesized)
+ std::swap(NewLHS, NewRHS);
+
+ ExprResult FinalRes =
+ S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL,
+ /*AllowRewrittenCandidates*/ false);
+ if (FinalRes.isInvalid())
+ return ExprError();
+ return new (S.Context) CXXRewrittenOperatorExpr(
+ (CXXRewrittenOperatorExpr::RewrittenOperatorKind)Ovl.getRewrittenKind(),
+ RewrittenRes.get(), FinalRes.get());
+}
+
+/// Rewritten candidates have been added but not checked for validity. They
+/// could still be non-viable if:
+/// (A) The rewritten call (x <=> y) is a builtin, but it will be ill-formed
+/// when built (for example it has narrowing conversions).
+/// (B) The expression (x <=> y) @ 0 is ill-formed for the result of (x <=> y).
+///
+/// If either is the case, this function should be considered non-viable and
+/// another best viable function needs to be computed.
+///
+/// Therefore, we do the following:
+/// (1) If we have no viable candidate, or a deleted candidate, stop.
+/// Otherwise, if we have a best viable candidate or a set of ambiguous
+/// candidates and none of them are rewritten, stop.
+///
+/// (2) If the best viable candidate is a rewritten candidate, build and
+/// check the full expression for that candidate. If it succeeds return
+/// the result. Otherwise, mark the candidate as non-viable, re-compute
+/// the best viable function, and continue.
+///
+/// (3) If we have ambiguity attempt to resolve it by evaluating each rewritten
+/// candidate causing ambiguity:
+///
+/// (3.1) build the full expression for the specified candidate.
+/// (3.2) If the result is invalid, mark the candidate as non-viable.
+///
+/// If only one viable candidate remains, stop. If the viable candidate is
+/// rewritten, return the previously computed full expression. Otherwise,
+/// if we have more than one viable candidate, stop. If no viable candidates
+/// remain from the initial set of equally ranked candidates, recompute the
+/// new best viable overload and continue.
+RewrittenOverloadResolver::CheckOverloadResult
+RewrittenOverloadResolver::RemoveNonViableRewrittenCandidates(
+ OverloadingResult OvlRes, OverloadCandidateSet::iterator Best,
+ ArrayRef<OverloadCandidate *> EquivCands, ExprResult &FinalResult) {
+ auto Success = [&](ExprResult Res) {
+ FinalResult = Res;
+ return Done;
+ };
+ switch (OvlRes) {
+ case OR_Deleted:
+ // FIXME(EricWF): If we've found a deleted rewritten operator, it's
+ // possible we should have never considered it a viable candidate.
+ case OR_No_Viable_Function:
+ return Done;
+
+ case OR_Success: {
+ OverloadCandidate &Ovl = *Best;
+ if (!Ovl.getRewrittenKind())
+ return Done;
+ // Build the full expression for the rewritten candidate, and return it if
+ // it's valid. Otherwise mark this candidate as non-viable and continue.
+ ExprResult Res = BuildRewrittenCandidate(Ovl);
+ if (Res.isInvalid()) {
+ Ovl.Viable = false;
+ return Continue;
+ }
+ return Success(Res);
+ }
+ case OR_Ambiguous: {
+ assert(EquivCands.size() > 1);
+ // If none of the viable candidates are rewritten, stop.
+ if (llvm::none_of(EquivCands, [](const OverloadCandidate *Cand) {
+ return (bool)Cand->getRewrittenKind();
+ }))
+ return Done;
+ int NumViableCandidates = 0;
+ ExprResult ViableRewritten = ExprError();
+ for (auto *Cand : EquivCands) {
+ OverloadCandidate &Ovl = *Cand;
+ if (Ovl.getRewrittenKind()) {
+ ExprResult Res = BuildRewrittenCandidate(Ovl);
+ if (Res.isInvalid()) {
+ Ovl.Viable = false;
+ continue;
+ }
+ ViableRewritten = Res;
+ }
+ ++NumViableCandidates;
+ }
+ // If only one of the candidates turns out to be viable, and it's a
+ // rewritten candidate, return that candidate as the result.
+ if (NumViableCandidates == 1 && !ViableRewritten.isInvalid())
+ return Success(ViableRewritten);
+ // If we still have ambiguity, return and let the caller diagnose it.
+ if (NumViableCandidates > 1)
+ return Done;
+ // All of the equally ranked candidates are invalid. Loop and try overload
+ // resolution again.
+ return Continue;
+ }
+ }
+ llvm_unreachable("unhandled case");
+}
+
/// \brief Create a binary operation that may resolve to an overloaded
/// operator.
///
@@ -12244,11 +12721,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 +12787,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 +12823,43 @@
// 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 (HasRewrittenCandidates) {
+ RewrittenOverloadResolver RewrittenOvlResolver(*this, OpLoc, Opc, Args, Fns,
+ PerformADL, CandidateSet);
- if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,
- FnDecl))
- return ExprError();
+ // Perform initial overload resolution that includes partially checked
+ // rewritten candidates, removing rewritten candidates which turn out to be
+ // invalid as needed.
+ ExprResult RewrittenResult =
+ RewrittenOvlResolver.ResolveRewrittenCandidates();
- 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;
- }
+ // If overload resolution was successful and the result was a re-written
+ // overload candidate, then that candidate was evaluated and we can return
+ // the result directly.
+ if (!RewrittenResult.isInvalid())
+ return RewrittenResult;
}
+ // Perform final overload resolution.
+ bool HadMultipleCandidates = (CandidateSet.size() > 1);
+ OverloadCandidateSet::iterator Best;
+ switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) {
+ case OR_Success:
+ assert(
+ !Best->getRewrittenKind() &&
+ "rewritten candidates should have already been resolved and evaluated");
+ 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 +12872,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 +12901,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 +12923,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
@@ -12372,7 +12372,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,12 @@
ID.AddInteger(S->getOperator());
}
+void StmtProfiler::VisitCXXRewrittenOperatorExpr(
+ const CXXRewrittenOperatorExpr *S) {
+ VisitExpr(S);
+ ID.AddInteger(S->getKind());
+}
+
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?
+ Visit(E->getOrigLHS());
+ OS << " " << BinaryOperator::getOpcodeStr(E->getOpcode()) << " ";
+ Visit(E->getOrigRHS());
+}
+
// C++ Coroutines TS
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -3559,6 +3559,11 @@
mangleExpression(cast<CXXStdInitializerListExpr>(E)->getSubExpr(), Arity);
break;
+ case Expr::CXXRewrittenOperatorExprClass:
+ mangleExpression(cast<CXXRewrittenOperatorExpr>(E)->getUnderlyingExpr(),
+ Arity);
+ 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
@@ -10858,6 +10862,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/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -3187,6 +3187,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.
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
@@ -2784,7 +2784,12 @@
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(),
@@ -2920,16 +2925,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
@@ -72,6 +72,17 @@
OCD_ViableCandidates
};
+ /// 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_Rewritten,
+ /// Both rewritten and synthesized.
+ ROC_Synthesized
+ };
+
/// ImplicitConversionKind - The kind of implicit conversion used to
/// convert an argument to a parameter's type. The enumerator values
/// match with the table titled 'Conversions' in [over.ics.scs] and are listed
@@ -755,21 +766,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 +827,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 +856,10 @@
return Function->getNumParams();
return ExplicitCallArguments;
}
+
+ RewrittenOverloadCandidateKind getRewrittenKind() const {
+ return static_cast<RewrittenOverloadCandidateKind>(RewrittenOpKind);
+ }
};
/// OverloadCandidateSet - A set of overload candidates, used in C++
@@ -853,8 +890,10 @@
private:
SmallVector<OverloadCandidate, 16> Candidates;
- llvm::SmallPtrSet<Decl *, 16> Functions;
+ using DeclSet = llvm::SmallPtrSet<Decl *, 16>;
+ DeclSet Functions;
+ private:
// Allocator for ConversionSequenceLists. We store the first few of these
// inline to avoid allocation for small sets.
llvm::BumpPtrAllocator SlabAllocator;
@@ -896,6 +935,31 @@
void destroyCandidates();
public:
+ /// \brief RewrittenCandidateContextGuard - Enter a context suitable for
+ /// adding rewritten overload candidates. Rewritten candidates can
+ /// re-consider previously seen functions, so save and clear the list of
+ /// considered functions, and restore it when the rewritten context is
+ /// exited.
+ struct RewrittenCandidateContextGuard {
+ RewrittenCandidateContextGuard(OverloadCandidateSet &CS)
+ : CandidateSet(CS) {
+ assert(CS.Kind == CSK_Operator &&
+ "rewritten expressions can only occur for operators");
+ OldFunctions = std::move(CandidateSet.Functions);
+ }
+
+ ~RewrittenCandidateContextGuard() {
+ CandidateSet.Functions.insert(OldFunctions.begin(), OldFunctions.end());
+ }
+
+ private:
+ OverloadCandidateSet &CandidateSet;
+ DeclSet OldFunctions;
+ };
+
+ friend struct RewrittenCandidateContextGuard;
+
+ public:
OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
: Loc(Loc), Kind(CSK) {}
OverloadCandidateSet(const OverloadCandidateSet &) = delete;
@@ -952,8 +1016,9 @@
}
/// Find the best viable function on this overload set, if it exists.
- OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
- OverloadCandidateSet::iterator& Best);
+ OverloadingResult BestViableFunction(
+ Sema &S, SourceLocation Loc, OverloadCandidateSet::iterator &Best,
+ SmallVectorImpl<OverloadCandidate *> *EquivalentCands = nullptr);
void NoteCandidates(Sema &S,
OverloadCandidateDisplayKind OCD,
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/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -2549,6 +2549,14 @@
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
DEF_TRAVERSE_STMT(AtomicExpr, {})
+DEF_TRAVERSE_STMT(CXXRewrittenOperatorExpr, {
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getUnderlyingExpr());
+ if (!getDerived().shouldVisitImplicitCode()) {
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRewrittenExpr());
+ }
+ ShouldVisitChildren = false;
+})
+
// For coroutines expressions, traverse either the operand
// as written or the implied calls, depending on what the
// derived class requests.
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -87,6 +87,8 @@
SourceRange getSourceRangeImpl() const LLVM_READONLY;
public:
+ friend class ASTReader;
+ friend class ASTWriter;
friend class ASTStmtReader;
friend class ASTStmtWriter;
@@ -4207,6 +4209,138 @@
child_range children() { return child_range(SubExprs, SubExprs + 2); }
};
+class CXXRewrittenOperatorExpr : public Expr {
+ friend class ASTReader;
+ friend class ASTWriter;
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
+
+ Stmt *SubExprs[2];
+
+ unsigned Kind : 2;
+
+ CXXRewrittenOperatorExpr(EmptyShell Empty)
+ : Expr(CXXRewrittenOperatorExprClass, Empty) {}
+
+public:
+ typedef BinaryOperatorKind Opcode;
+ enum RewrittenOperatorKind { ROC_None, ROC_Rewritten, ROC_Synthesized };
+
+public:
+ // FIXME(EricWF): Figure out if this will even be built for dependent
+ // expressions.
+ CXXRewrittenOperatorExpr(RewrittenOperatorKind Kind, Expr *Underlying,
+ Expr *Rewritten)
+ : Expr(CXXRewrittenOperatorExprClass, Rewritten->getType(),
+ Rewritten->getValueKind(), Rewritten->getObjectKind(),
+ /*Dependent*/ false, false, false, false),
+ Kind(Kind) {
+ SubExprs[0] = Underlying;
+ SubExprs[1] = Rewritten;
+ }
+
+ Expr *getUnderlyingExpr() const {
+ return static_cast<Expr *>(SubExprs[0]);
+ }
+ Expr *getRewrittenExpr() const {
+ return static_cast<Expr *>(SubExprs[1]);
+ }
+
+ void setKind(RewrittenOperatorKind xKind) { Kind = xKind; }
+ RewrittenOperatorKind getKind() const {
+ return static_cast<RewrittenOperatorKind>(Kind);
+ }
+ bool isReverseOrder() const LLVM_READONLY {
+ return getKind() == ROC_Synthesized;
+ }
+
+ static Expr *getLHSExpr(Expr *E) {
+ if (auto *UE = dyn_cast<BinaryOperator>(E))
+ return UE->getLHS();
+ else
+ return cast<CXXOperatorCallExpr>(E)->getArg(0);
+ }
+ static Expr *getRHSExpr(Expr *E) {
+ if (auto *UE = dyn_cast<BinaryOperator>(E))
+ return UE->getRHS();
+ else
+ return cast<CXXOperatorCallExpr>(E)->getArg(1);
+ }
+
+ Expr *getOrigLHS() const {
+ if (!isReverseOrder())
+ return getLHSExpr(getLHSExpr(getRewrittenExpr()));
+ else
+ return getRHSExpr(getRHSExpr(getRewrittenExpr()));
+ }
+
+ Expr *getOrigRHS() const {
+ if (!isReverseOrder())
+ return getRHSExpr(getLHSExpr(getRewrittenExpr()));
+ else
+ return getLHSExpr(getRHSExpr(getRewrittenExpr()));
+ }
+
+ static Opcode getOpcodeFromExpr(Expr *E) {
+ if (auto *UE = dyn_cast<BinaryOperator>(E))
+ return UE->getOpcode();
+ else
+ return BinaryOperator::getOverloadedOpcode(
+ cast<CXXOperatorCallExpr>(E)->getOperator());
+ }
+
+ Opcode getOrigOpcode() const;
+
+ Expr *getLHS() const { return getLHSExpr(getRewrittenExpr()); }
+ Expr *getRHS() const { return getRHSExpr(getRewrittenExpr()); }
+ Opcode getOpcode() const { return getOpcodeFromExpr(getRewrittenExpr()); }
+
+ // Forwarders to the getUnderlyingExpr() expression
+
+ SourceLocation getLocStart() const {
+ Expr *Underlying = getUnderlyingExpr();
+ if (auto *UE = dyn_cast<BinaryOperator>(Underlying)) {
+ return UE->getLocStart();
+ } else {
+ assert(isa<CXXOperatorCallExpr>(Underlying));
+ return cast<CXXOperatorCallExpr>(Underlying)->getLocStart();
+ }
+ }
+ SourceLocation getLocEnd() const {
+ Expr *Underlying = getUnderlyingExpr();
+ if (auto *UE = dyn_cast<BinaryOperator>(Underlying)) {
+ return UE->getLocEnd();
+ } else {
+ assert(isa<CXXOperatorCallExpr>(Underlying));
+ return cast<CXXOperatorCallExpr>(Underlying)->getLocEnd();
+ }
+ }
+ SourceLocation getExprLoc() const {
+ Expr *Underlying = getUnderlyingExpr();
+ if (auto *UE = dyn_cast<BinaryOperator>(Underlying)) {
+ return UE->getExprLoc();
+ } else {
+ assert(isa<CXXOperatorCallExpr>(Underlying));
+ return cast<CXXOperatorCallExpr>(Underlying)->getExprLoc();
+ }
+ }
+ SourceLocation getOperatorLoc() const {
+ Expr *Underlying = getUnderlyingExpr();
+ if (auto *UE = dyn_cast<BinaryOperator>(Underlying)) {
+ return UE->getOperatorLoc();
+ } else {
+ assert(isa<CXXOperatorCallExpr>(Underlying));
+ return cast<CXXOperatorCallExpr>(Underlying)->getOperatorLoc();
+ }
+ }
+
+ child_range children() { return child_range(SubExprs, SubExprs + 2); }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXRewrittenOperatorExprClass;
+ }
+};
+
/// \brief Represents an expression that might suspend coroutine execution;
/// either a co_await or co_yield expression.
///
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits