EricWF updated this revision to Diff 146090.
EricWF added a comment.
Remove a bunch of nonsense this patch originally had. Specifically remove the
bits of overload resolution which disambiguated rewritten expressions after the
fact; Instead attempt to validate that the return type of the three-way
comparison operand in a rewritten relational or equality can be used with the
the specified operand (e.g. `std::strong_equality` cannot be used with a
relational operator).
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/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/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 {};
+// 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;
+struct T {
+ std::strong_ordering operator<=>(U const &RHS) const;
+};
+struct U {
+ std::strong_ordering operator<=>(T const &RHS) const;
+};
+
+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() {
+ // expected-error@+1 {{use of overloaded operator '<' is ambiguous}}
+ (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::CXXRewrittenExprClass:
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,21 @@
Code = serialization::EXPR_CXX_FOLD;
}
+void ASTStmtWriter::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) {
+ VisitExpr(E);
+ Record.push_back(E->getRewrittenKind());
+ Record.AddStmt(E->SubExprs[0]);
+ Record.AddStmt(E->SubExprs[1]);
+ switch (E->getRewrittenKind()) {
+ case CXXRewrittenExpr::Comparison: {
+ CXXRewrittenExpr::ComparisonBits Bits = E->ExtraBits.CompareBits;
+ Record.push_back(Bits.IsSynthesized);
+ break;
+ }
+ }
+ 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,21 @@
E->Opcode = (BinaryOperatorKind)Record.readInt();
}
+void ASTStmtReader::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) {
+ VisitExpr(E);
+ E->setRewrittenKind(
+ static_cast<CXXRewrittenExpr::RewrittenKind>(Record.readInt()));
+ E->SubExprs[0] = Record.readSubExpr();
+ E->SubExprs[1] = Record.readSubExpr();
+ switch (E->getRewrittenKind()) {
+ case CXXRewrittenExpr::Comparison: {
+ CXXRewrittenExpr::ComparisonBits &Bits = E->ExtraBits.CompareBits;
+ Bits.IsSynthesized = Record.readInt();
+ break;
+ }
+ }
+}
+
void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
VisitExpr(E);
E->SourceExpr = Record.readSubExpr();
@@ -4074,6 +4089,9 @@
S = new (Context) CXXFoldExpr(Empty);
break;
+ case EXPR_CXX_REWRITTEN_OPERATOR:
+ S = new (Context) CXXRewrittenExpr(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,14 @@
return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator);
}
+ ExprResult
+ RebuildCXXRewrittenExpr(CXXRewrittenExpr::RewrittenKind Kind, Expr *Original,
+ Expr *Rewritten,
+ CXXRewrittenExpr::ExtraRewrittenBits ExtraBits) {
+ return new (SemaRef.Context)
+ CXXRewrittenExpr(Kind, Original, Rewritten, ExtraBits);
+ }
+
/// Build a new atomic operation expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -11515,6 +11523,68 @@
return getDerived().TransformExpr(E->GetTemporaryExpr());
}
+static Expr *extractOperand(Expr *E, unsigned Idx) {
+ assert(Idx < 2);
+ if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+ if (Idx == 0)
+ return BO->getLHS();
+ return BO->getRHS();
+ }
+ if (auto *CE = dyn_cast<CallExpr>(E)) {
+ assert(CE->getNumArgs() == 2);
+ return CE->getArg(Idx);
+ }
+ llvm_unreachable("unhandled case");
+}
+static std::pair<Expr *, Expr *>
+extractOriginalOperandsFromRewrittenComparison(Expr *E, bool IsThreeWay,
+ bool IsSynthesized) {
+ if (IsThreeWay)
+ return {extractOperand(E, IsSynthesized ? 1 : 0),
+ extractOperand(E, IsSynthesized ? 0 : 1)};
+ return extractOriginalOperandsFromRewrittenComparison(
+ extractOperand(E, IsSynthesized ? 1 : 0), true, IsSynthesized);
+}
+
+template <typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformCXXRewrittenExpr(CXXRewrittenExpr *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;
+
+ Expr *Original;
+ switch (E->getRewrittenKind()) {
+ case CXXRewrittenExpr::Comparison: {
+ BinaryOperator *Op = cast<BinaryOperator>(E->getOriginalExpr());
+
+ // Extract the already transformed operands from the rewritten expression.
+ std::pair<Expr *, Expr *> OrigArgs =
+ extractOriginalOperandsFromRewrittenComparison(
+ Rewritten, Op->getOpcode() == BO_Cmp,
+ E->getRewrittenInfo()->CompareBits.IsSynthesized);
+
+ // Build a dummy node representing the expression as written.
+ Original = new (SemaRef.Context) BinaryOperator(
+ OpaqueValueExpr::Create(SemaRef.Context, OrigArgs.first),
+ OpaqueValueExpr::Create(SemaRef.Context, OrigArgs.second),
+ Op->getOpcode(), Rewritten->getType(), Rewritten->getValueKind(),
+ Rewritten->getObjectKind(), Op->getOperatorLoc(), Op->getFPFeatures());
+ break;
+ }
+ }
+ return getDerived().RebuildCXXRewrittenExpr(
+ E->getRewrittenKind(), Original, Rewritten, *E->getRewrittenInfo());
+}
+
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
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();
@@ -7186,7 +7207,7 @@
void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
SourceLocation OpLoc,
ArrayRef<Expr *> Args,
- OverloadCandidateSet& CandidateSet,
+ OverloadCandidateSet &CandidateSet,
SourceRange OpRange) {
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);
@@ -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,109 @@
}
}
+
+/// 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::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 (IsRelationalOrEquality) {
+ if (FunctionDecl *FD = Ovl.Function) {
+ if (FD->getReturnType()->isUndeducedType()) {
+ if (DeduceReturnType(FD, OpLoc)) {
+ Ovl.Viable = false;
+ continue;
+ }
+ }
+ QualType RetTy = FD->getReturnType();
+ if (const ComparisonCategoryInfo *Info =
+ Context.CompCategories.lookupInfoForType(RetTy)) {
+ if (!Info->isUsableWithOperator(Opc)) {
+ Ovl.Viable = false;
+ continue;
+ }
+ } else {
+ // FIXME(EricWF): Check that the return type can be used with
+ // the specified relational operator
+ }
+ } else {
+ Optional<ComparisonCategoryType> CompType =
+ ComparisonCategories::computeComparisonTypeForBuiltin(
+ Ovl.BuiltinParamTypes[0], Ovl.BuiltinParamTypes[1]);
+ if (!CompType ||
+ !ComparisonCategoryInfo::isUsableWithOperator(*CompType, Opc)) {
+ Ovl.Viable = false;
+ continue;
+ }
+ }
+ }
+ }
+ };
+
+ // 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);
+}
+
/// 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,
- TemplateArgumentListInfo *ExplicitTemplateArgs,
- OverloadCandidateSet& CandidateSet,
- bool PartialOverloading) {
+void Sema::AddArgumentDependentLookupCandidates(
+ DeclarationName Name, SourceLocation Loc, ArrayRef<Expr *> Args,
+ TemplateArgumentListInfo *ExplicitTemplateArgs,
+ OverloadCandidateSet &CandidateSet, bool PartialOverloading) {
ADLResult Fns;
// FIXME: This approach for uniquing ADL results (and removing
@@ -9008,8 +9119,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 +9137,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 +9244,50 @@
// 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_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) {
+ SmallVector<QualType, 2> RevTypes(Types.rbegin(), Types.rend());
+ return RevTypes;
+ }
+ return Types;
+ };
+ if (GetParamTypes(Cand1) == GetParamTypes(Cand2))
+ return C2Roc == ROC_Synthesized;
+ }
+ }
+ }
+
// Check C++17 tie-breakers for deduction guides.
{
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
@@ -12228,6 +12383,175 @@
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_Synthesized;
+ 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_Synthesized)
+ std::swap(NewLHS, NewRHS);
+
+ RewrittenRes =
+ S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL,
+ /*AllowRewrittenCandidates*/ false);
+ if (RewrittenRes.isInvalid())
+ return ExprError();
+ }
+ Expr *Rewritten = RewrittenRes.get();
+
+ // Create a dummy expression representing the original expression as written.
+ // FIXME(EricWF): This doesn't actually really represent the expression as
+ // written, because it may not result in a to a builtin operator.
+ Expr *Original = new (S.Context)
+ BinaryOperator(OpaqueValueExpr::Create(S.Context, Args[0]),
+ OpaqueValueExpr::Create(S.Context, Args[1]), Opc,
+ Rewritten->getType(), Rewritten->getValueKind(),
+ Rewritten->getObjectKind(), OpLoc, S.FPFeatures);
+
+ CXXRewrittenExpr::ExtraRewrittenBits ExtraBits;
+ ExtraBits.CompareBits.IsSynthesized = IsSynthesized;
+ return new (S.Context) CXXRewrittenExpr(CXXRewrittenExpr::Comparison,
+ Original, Rewritten, ExtraBits);
+}
+
/// Create a binary operation that may resolve to an overloaded
/// operator.
///
@@ -12244,11 +12568,11 @@
///
/// \param LHS Left-hand argument.
/// \param RHS Right-hand argument.
-ExprResult
-Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
- BinaryOperatorKind Opc,
- const UnresolvedSetImpl &Fns,
- Expr *LHS, Expr *RHS, bool PerformADL) {
+ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
+ BinaryOperatorKind Opc,
+ 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 +12634,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,191 +12670,98 @@
// 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>();
- }
-
- // 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);
- }
+ // 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();
+ }
- // 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();
+ // 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
+ // viable functions, then the operator is assumed to be the
+ // built-in operator and interpreted according to clause 5.
+ if (Opc == BO_Comma)
+ break;
- ExprResult ArgsRes1 =
- PerformImplicitConversion(Args[1], Best->BuiltinParamTypes[1],
- Best->Conversions[1], AA_Passing);
- if (ArgsRes1.isInvalid())
- return ExprError();
- Args[1] = ArgsRes1.get();
- break;
+ // For class as left operand for assignment or compound assignment
+ // 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) {
+ Diag(OpLoc, diag::err_ovl_no_viable_oper)
+ << 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();
}
- }
-
- case OR_No_Viable_Function: {
- // C++ [over.match.oper]p9:
- // If the operator is the operator , [...] and there are no
- // viable functions, then the operator is assumed to be the
- // built-in operator and interpreted according to clause 5.
- if (Opc == BO_Comma)
- break;
-
- // For class as left operand for assignment or compound assignment
- // 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) {
- Diag(OpLoc, diag::err_ovl_no_viable_oper)
- << 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();
- }
- } else {
- // This is an erroneous use of an operator which can be overloaded by
- // a non-member function. Check for non-member operators which were
- // defined too late to be candidates.
- if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args))
- // FIXME: Recover by calling the found function.
- return ExprError();
+ } else {
+ // This is an erroneous use of an operator which can be overloaded by
+ // a non-member function. Check for non-member operators which were
+ // defined too late to be candidates.
+ if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args))
+ // FIXME: Recover by calling the found function.
+ return ExprError();
- // No viable function; try to create a built-in operation, which will
- // produce an error. Then, show the non-viable candidates.
- Result = CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
- }
- assert(Result.isInvalid() &&
- "C++ binary operator overloading is missing candidates!");
- if (Result.isInvalid())
- CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
- BinaryOperator::getOpcodeStr(Opc), OpLoc);
- return Result;
+ // No viable function; try to create a built-in operation, which will
+ // produce an error. Then, show the non-viable candidates.
+ Result = CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
}
-
- 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();
- CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args,
+ assert(Result.isInvalid() &&
+ "C++ binary operator overloading is missing candidates!");
+ if (Result.isInvalid())
+ CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
BinaryOperator::getOpcodeStr(Opc), OpLoc);
- return ExprError();
+ 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();
+ CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args,
+ BinaryOperator::getOpcodeStr(Opc), OpLoc);
+ return ExprError();
- case OR_Deleted:
- if (isImplicitlyDeleted(Best->Function)) {
- CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
- Diag(OpLoc, diag::err_ovl_deleted_special_oper)
+ case OR_Deleted:
+ if (isImplicitlyDeleted(Best->Function)) {
+ CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
+ Diag(OpLoc, diag::err_ovl_deleted_special_oper)
<< Context.getRecordType(Method->getParent())
<< getSpecialMember(Method);
- // The user probably meant to call this special member. Just
- // explain why it's deleted.
- NoteDeletedFunction(Method);
- return ExprError();
- } else {
- Diag(OpLoc, diag::err_ovl_deleted_oper)
- << Best->Function->isDeleted()
- << BinaryOperator::getOpcodeStr(Opc)
+ // The user probably meant to call this special member. Just
+ // explain why it's deleted.
+ NoteDeletedFunction(Method);
+ return ExprError();
+ } else {
+ Diag(OpLoc, diag::err_ovl_deleted_oper)
+ << Best->Function->isDeleted() << BinaryOperator::getOpcodeStr(Opc)
<< getDeletedOrUnavailableSuffix(Best->Function)
<< Args[0]->getSourceRange() << Args[1]->getSourceRange();
- }
- CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
- BinaryOperator::getOpcodeStr(Opc), OpLoc);
- return ExprError();
+ }
+ CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
+ BinaryOperator::getOpcodeStr(Opc), OpLoc);
+ return ExprError();
}
// We matched a built-in operator; build it.
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");
-
- 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");
- }();
+ Optional<ComparisonCategoryType> TypeKind =
+ ComparisonCategories::computeComparisonTypeForBuiltin(Type);
+ assert(TypeKind && "composite type for <=> has not been set");
- 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.
- return InvalidOperands(Loc, LHS, RHS);
+ 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::CXXRewrittenExprClass:
+ return canThrow(cast<CXXRewrittenExpr>(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 *VisitCXXRewrittenExpr(const CXXRewrittenExpr *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,11 @@
ID.AddInteger(S->getOperator());
}
+void StmtProfiler::VisitCXXRewrittenExpr(const CXXRewrittenExpr *S) {
+ VisitExpr(S);
+ 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,13 @@
OS << ")";
}
+void StmtPrinter::VisitCXXRewrittenExpr(CXXRewrittenExpr *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->getOriginalExpr());
+}
+
// 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,10 @@
mangleExpression(cast<CXXStdInitializerListExpr>(E)->getSubExpr(), Arity);
break;
+ case Expr::CXXRewrittenExprClass:
+ mangleExpression(cast<CXXRewrittenExpr>(E)->getOriginalExpr(), 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 VisitCXXRewrittenExpr(const CXXRewrittenExpr *E) {
+ return StmtVisitorTy::Visit(E->getRewrittenExpr());
+ }
};
} // namespace
@@ -10859,6 +10863,8 @@
case Expr::ChooseExprClass: {
return CheckICE(cast<ChooseExpr>(E)->getChosenSubExpr(), Ctx);
}
+ case Expr::CXXRewrittenExprClass:
+ return CheckICE(cast<CXXRewrittenExpr>(E)->getRewrittenExpr(), Ctx);
}
llvm_unreachable("Invalid StmtClass!");
Index: lib/AST/ExprClassification.cpp
===================================================================
--- lib/AST/ExprClassification.cpp
+++ lib/AST/ExprClassification.cpp
@@ -287,7 +287,8 @@
if (cast<GenericSelectionExpr>(E)->isResultDependent())
return Cl::CL_PRValue;
return ClassifyInternal(Ctx,cast<GenericSelectionExpr>(E)->getResultExpr());
-
+ case Expr::CXXRewrittenExprClass:
+ return ClassifyInternal(Ctx, cast<CXXRewrittenExpr>(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
@@ -3188,6 +3188,11 @@
return false;
}
+ case CXXRewrittenExprClass: {
+ const auto *RO = cast<CXXRewrittenExpr>(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,127 @@
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;
+}
+
+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;
+ // Count the number of times each comparison category type occurs in the
+ // specified type list. 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. If any type is not a comparison category, return
+ // nullptr.
+ for (auto TyKind : Types) {
+ // --- If any T is not a comparison category type, U is void.
+ 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;
+
+ // FIXME(EricWF): What if we don't find std::strong_ordering
+ // --- 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
@@ -1666,7 +1666,7 @@
EXPR_OBJC_BOXED_EXPRESSION,
EXPR_OBJC_ARRAY_LITERAL,
EXPR_OBJC_DICTIONARY_LITERAL,
-
+
/// An ObjCEncodeExpr record.
EXPR_OBJC_ENCODE,
@@ -1725,7 +1725,7 @@
EXPR_OBJC_AVAILABILITY_CHECK,
// C++
-
+
/// A CXXCatchStmt record.
STMT_CXX_CATCH,
@@ -1774,59 +1774,60 @@
/// A CXXBoolLiteralExpr record.
EXPR_CXX_BOOL_LITERAL,
- EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr
- EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr).
- EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type).
- EXPR_CXX_THIS, // CXXThisExpr
- EXPR_CXX_THROW, // CXXThrowExpr
- EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr
- EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr
- EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr
+ EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr
+ EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr).
+ EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type).
+ EXPR_CXX_THIS, // CXXThisExpr
+ EXPR_CXX_THROW, // CXXThrowExpr
+ EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr
+ EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr
+ EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr
EXPR_CXX_SCALAR_VALUE_INIT, // CXXScalarValueInitExpr
EXPR_CXX_NEW, // CXXNewExpr
EXPR_CXX_DELETE, // CXXDeleteExpr
EXPR_CXX_PSEUDO_DESTRUCTOR, // CXXPseudoDestructorExpr
-
- EXPR_EXPR_WITH_CLEANUPS, // ExprWithCleanups
-
+
+ EXPR_EXPR_WITH_CLEANUPS, // ExprWithCleanups
+
EXPR_CXX_DEPENDENT_SCOPE_MEMBER, // CXXDependentScopeMemberExpr
EXPR_CXX_DEPENDENT_SCOPE_DECL_REF, // DependentScopeDeclRefExpr
EXPR_CXX_UNRESOLVED_CONSTRUCT, // CXXUnresolvedConstructExpr
EXPR_CXX_UNRESOLVED_MEMBER, // UnresolvedMemberExpr
EXPR_CXX_UNRESOLVED_LOOKUP, // UnresolvedLookupExpr
- EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr
- EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr
+ EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr
+ EXPR_CXX_NOEXCEPT, // CXXNoexceptExpr
- EXPR_OPAQUE_VALUE, // OpaqueValueExpr
- EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator
- EXPR_TYPE_TRAIT, // TypeTraitExpr
- EXPR_ARRAY_TYPE_TRAIT, // ArrayTypeTraitIntExpr
-
- EXPR_PACK_EXPANSION, // PackExpansionExpr
- EXPR_SIZEOF_PACK, // SizeOfPackExpr
- EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr
- EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr
- EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr
- EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
- EXPR_CXX_FOLD, // CXXFoldExpr
+ EXPR_OPAQUE_VALUE, // OpaqueValueExpr
+ EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator
+ EXPR_TYPE_TRAIT, // TypeTraitExpr
+ EXPR_ARRAY_TYPE_TRAIT, // ArrayTypeTraitIntExpr
+
+ EXPR_PACK_EXPANSION, // PackExpansionExpr
+ EXPR_SIZEOF_PACK, // SizeOfPackExpr
+ EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr
+ EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
+ EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr
+ EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
+ EXPR_CXX_FOLD, // CXXFoldExpr
+ EXPR_CXX_REWRITTEN_OPERATOR, // CXXRewrittenExpr
// CUDA
- EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
+ EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
// OpenCL
- EXPR_ASTYPE, // AsTypeExpr
+ EXPR_ASTYPE, // AsTypeExpr
// Microsoft
- EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr
+ EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr
EXPR_CXX_PROPERTY_SUBSCRIPT_EXPR, // MSPropertySubscriptExpr
- EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr).
- EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type).
- STMT_SEH_LEAVE, // SEHLeaveStmt
- STMT_SEH_EXCEPT, // SEHExceptStmt
- STMT_SEH_FINALLY, // SEHFinallyStmt
- STMT_SEH_TRY, // SEHTryStmt
+ EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr).
+ EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type).
+ STMT_SEH_LEAVE, // SEHLeaveStmt
+ STMT_SEH_EXCEPT, // SEHExceptStmt
+ STMT_SEH_FINALLY, // SEHFinallyStmt
+ STMT_SEH_TRY, // SEHTryStmt
// OpenMP directives
STMT_OMP_PARALLEL_DIRECTIVE,
@@ -1879,10 +1880,10 @@
EXPR_OMP_ARRAY_SECTION,
// ARC
- EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr
+ EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr
- STMT_MS_DEPENDENT_EXISTS, // MSDependentExistsStmt
- EXPR_LAMBDA, // LambdaExpr
+ STMT_MS_DEPENDENT_EXISTS, // MSDependentExistsStmt
+ EXPR_LAMBDA, // LambdaExpr
STMT_COROUTINE_BODY,
STMT_CORETURN,
EXPR_COAWAIT,
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
@@ -62,7 +62,7 @@
/// Succeeded, but refers to a deleted function.
OR_Deleted
};
-
+
enum OverloadCandidateDisplayKind {
/// Requests that all candidates be shown. Viable candidates will
/// be printed first.
@@ -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
@@ -107,7 +118,7 @@
/// Integral conversions (C++ [conv.integral])
ICK_Integral_Conversion,
- /// Floating point conversions (C++ [conv.double]
+ /// Floating point conversions (C++ [conv.double]
ICK_Floating_Conversion,
/// Complex conversions (C99 6.3.1.6)
@@ -252,7 +263,7 @@
/// Whether the qualification conversion involves a change in the
/// Objective-C lifetime (for automatic reference counting).
unsigned QualificationIncludesObjCLifetime : 1;
-
+
/// IncompatibleObjC - Whether this is an Objective-C conversion
/// that we should warn about (if we actually use it).
unsigned IncompatibleObjC : 1;
@@ -282,7 +293,7 @@
/// Whether this binds a reference to an object with a different
/// Objective-C lifetime qualifier.
unsigned ObjCLifetimeConversionBinding : 1;
-
+
/// FromType - The type that this conversion is converting
/// from. This is an opaque pointer that can be translated into a
/// QualType.
@@ -303,13 +314,13 @@
void setFromType(QualType T) { FromTypePtr = T.getAsOpaquePtr(); }
- void setToType(unsigned Idx, QualType T) {
+ void setToType(unsigned Idx, QualType T) {
assert(Idx < 3 && "To type index is out of range");
- ToTypePtrs[Idx] = T.getAsOpaquePtr();
+ ToTypePtrs[Idx] = T.getAsOpaquePtr();
}
void setAllToTypes(QualType T) {
- ToTypePtrs[0] = T.getAsOpaquePtr();
+ ToTypePtrs[0] = T.getAsOpaquePtr();
ToTypePtrs[1] = ToTypePtrs[0];
ToTypePtrs[2] = ToTypePtrs[0];
}
@@ -324,11 +335,11 @@
}
void setAsIdentityConversion();
-
+
bool isIdentityConversion() const {
return Second == ICK_Identity && Third == ICK_Identity;
}
-
+
ImplicitConversionRank getRank() const;
NarrowingKind
getNarrowingKind(ASTContext &Context, const Expr *Converted,
@@ -562,16 +573,16 @@
new (this) ImplicitConversionSequence(Other);
return *this;
}
-
+
~ImplicitConversionSequence() {
destruct();
}
Kind getKind() const {
assert(isInitialized() && "querying uninitialized conversion");
return Kind(ConversionKind);
}
-
+
/// Return a ranking of the implicit conversion sequence
/// kind, where smaller ranks represent better conversion
/// sequences.
@@ -581,11 +592,11 @@
/// per C++ [over.best.ics]p10.
unsigned getKindRank() const {
switch (getKind()) {
- case StandardConversion:
+ case StandardConversion:
return 0;
case UserDefinedConversion:
- case AmbiguousConversion:
+ case AmbiguousConversion:
return 1;
case EllipsisConversion:
@@ -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.
@@ -781,7 +796,7 @@
union {
DeductionFailureInfo DeductionFailure;
-
+
/// FinalConversion - For a conversion function (where Function is
/// a CXXConversionDecl), the standard conversion that occurs
/// after the call to the overload candidate to convert the result
@@ -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;
@@ -953,7 +1017,7 @@
/// Find the best viable function on this overload set, if it exists.
OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
- OverloadCandidateSet::iterator& Best);
+ OverloadCandidateSet::iterator &Best);
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 CXXRewrittenExpr : DStmt<Expr>;
// C++ Coroutines TS expressions
def CoroutineSuspendExpr : DStmt<Expr, 1>;
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -246,6 +246,13 @@
/// bit is set to true.
unsigned IsUnique : 1;
};
+ class CXXRewrittenExprBitfields {
+ friend class CXXRewrittenExpr;
+
+ unsigned : NumExprBits;
+
+ unsigned Kind : 1;
+ };
class ObjCIndirectCopyRestoreExprBitfields {
friend class ObjCIndirectCopyRestoreExpr;
@@ -309,6 +316,7 @@
InitListExprBitfields InitListExprBits;
TypeTraitExprBitfields TypeTraitExprBits;
CoawaitExprBitfields CoawaitBits;
+ CXXRewrittenExprBitfields CXXRewrittenBits;
};
public:
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(CXXRewrittenExpr, {
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOriginalExpr());
+ 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,73 @@
child_range children() { return child_range(SubExprs, SubExprs + 2); }
};
+
+class CXXRewrittenExpr : public Expr {
+
+public:
+ enum RewrittenKind { Comparison };
+
+ struct ComparisonBits {
+ /// Whether this rewritten comparison expression has reverse-order
+ /// parameters.
+ unsigned IsSynthesized : 1;
+ };
+
+ union ExtraRewrittenBits {
+ ComparisonBits CompareBits;
+ };
+
+private:
+ friend class ASTReader;
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
+
+ Stmt *SubExprs[2];
+ ExtraRewrittenBits ExtraBits;
+
+ CXXRewrittenExpr(EmptyShell Empty) : Expr(CXXRewrittenExprClass, Empty) {}
+
+public:
+ CXXRewrittenExpr(RewrittenKind Kind, Expr *Original, Expr *Rewritten,
+ ExtraRewrittenBits ExtraBits)
+ : Expr(CXXRewrittenExprClass, Rewritten->getType(),
+ Rewritten->getValueKind(), Rewritten->getObjectKind(),
+ /*Dependent*/ Rewritten->isTypeDependent(),
+ Rewritten->isValueDependent(),
+ Original->isInstantiationDependent(),
+ Rewritten->containsUnexpandedParameterPack()),
+ ExtraBits(ExtraBits) {
+ SubExprs[0] = Original;
+ SubExprs[1] = Rewritten;
+ }
+
+ RewrittenKind getRewrittenKind() const {
+ return static_cast<RewrittenKind>(CXXRewrittenBits.Kind);
+ }
+ void setRewrittenKind(RewrittenKind Kind) { CXXRewrittenBits.Kind = Kind; }
+
+ ExtraRewrittenBits *getRewrittenInfo() { return &ExtraBits; }
+ const ExtraRewrittenBits *getRewrittenInfo() const { return &ExtraBits; }
+
+ Expr *getOriginalExpr() const { return static_cast<Expr *>(SubExprs[0]); }
+ Expr *getRewrittenExpr() const {
+ return static_cast<Expr *>(SubExprs[1]);
+ }
+
+ SourceLocation getLocStart() const {
+ return getOriginalExpr()->getLocStart();
+ }
+ SourceLocation getLocEnd() const { return getOriginalExpr()->getLocEnd(); }
+ SourceLocation getExprLoc() const { return getOriginalExpr()->getExprLoc(); }
+
+ child_range children() { return child_range(SubExprs, SubExprs + 2); }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXRewrittenExprClass;
+ }
+};
+
+
/// 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"
@@ -127,28 +128,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 +204,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 +246,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 +261,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