diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 2a6216a67e381..f193f959d3a6c 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -2189,6 +2189,31 @@ bool ByteCodeExprGen::VisitCXXRewrittenBinaryOperator( return this->delegate(E->getSemanticForm()); } +template +bool ByteCodeExprGen::VisitPseudoObjectExpr( + const PseudoObjectExpr *E) { + + for (const Expr *SemE : E->semantics()) { + if (auto *OVE = dyn_cast(SemE)) { + if (SemE == E->getResultExpr()) + return false; + + if (OVE->isUnique()) + continue; + + if (!this->discard(OVE)) + return false; + } else if (SemE == E->getResultExpr()) { + if (!this->delegate(SemE)) + return false; + } else { + if (!this->discard(SemE)) + return false; + } + } + return true; +} + template bool ByteCodeExprGen::discard(const Expr *E) { if (E->containsErrors()) return false; diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 8769d1d7b8502..5b3b533dba387 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -117,6 +117,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, bool VisitRequiresExpr(const RequiresExpr *E); bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *E); + bool VisitPseudoObjectExpr(const PseudoObjectExpr *E); protected: bool visitExpr(const Expr *E) override; diff --git a/clang/test/AST/Interp/spaceship.cpp b/clang/test/AST/Interp/spaceship.cpp new file mode 100644 index 0000000000000..7495b893772e8 --- /dev/null +++ b/clang/test/AST/Interp/spaceship.cpp @@ -0,0 +1,232 @@ +// RUN: %clang_cc1 -std=c++2a -verify=both,ref %s -fcxx-exceptions +// RUN: %clang_cc1 -std=c++2a -verify=both,expected %s -fcxx-exceptions -fexperimental-new-constant-interpreter + +namespace std { + struct strong_ordering { // both-note 6{{candidate}} + int n; + constexpr operator int() const { return n; } + static const strong_ordering less, equal, greater; + }; + constexpr strong_ordering strong_ordering::less{-1}, + strong_ordering::equal{0}, strong_ordering::greater{1}; + + struct weak_ordering { + int n; + constexpr weak_ordering(int n) : n(n) {} + constexpr weak_ordering(strong_ordering o) : n(o.n) {} + constexpr operator int() const { return n; } + static const weak_ordering less, equivalent, greater; + }; + constexpr weak_ordering weak_ordering::less{-1}, + weak_ordering::equivalent{0}, weak_ordering::greater{1}; + + struct partial_ordering { + double d; + constexpr partial_ordering(double d) : d(d) {} + constexpr partial_ordering(strong_ordering o) : d(o.n) {} + constexpr partial_ordering(weak_ordering o) : d(o.n) {} + constexpr operator double() const { return d; } + static const partial_ordering less, equivalent, greater, unordered; + }; + constexpr partial_ordering partial_ordering::less{-1}, + partial_ordering::equivalent{0}, partial_ordering::greater{1}, + partial_ordering::unordered{__builtin_nan("")}; + + static_assert(!(partial_ordering::unordered < 0)); + static_assert(!(partial_ordering::unordered == 0)); + static_assert(!(partial_ordering::unordered > 0)); +} + +namespace Deletedness { + struct A { + std::strong_ordering operator<=>(const A&) const; + }; + struct B { + bool operator==(const B&) const; + bool operator<(const B&) const; + }; + struct C { + std::strong_ordering operator<=>(const C&) const = delete; // both-note 2{{deleted}} + }; + struct D1 { + bool operator==(const D1&) const; + std::strong_ordering operator<=>(int) const; // both-note 2{{function not viable}} both-note 2{{function (with reversed parameter order) not viable}} + bool operator<(int) const; // both-note 2{{function not viable}} + }; + struct D2 { + bool operator<(const D2&) const; + std::strong_ordering operator<=>(int) const; // both-note 2{{function not viable}} both-note 2{{function (with reversed parameter order) not viable}} + bool operator==(int) const; // both-note 2{{function not viable}} + }; + struct E { + bool operator==(const E&) const; + bool operator<(const E&) const = delete; // both-note 2{{deleted}} + }; + struct F { + std::strong_ordering operator<=>(const F&) const; // both-note 2{{candidate}} + std::strong_ordering operator<=>(F) const; // both-note 2{{candidate}} + }; + struct G1 { + bool operator==(const G1&) const; + void operator<(const G1&) const; + }; + struct G2 { + void operator==(const G2&) const; + bool operator<(const G2&) const; + }; + struct H { + void operator<=>(const H&) const; + }; + + // both-note@#base {{deleted comparison function for base class 'C'}} + // both-note@#base {{no viable three-way comparison function for base class 'D1'}} + // both-note@#base {{three-way comparison cannot be synthesized because there is no viable function for '<' comparison}} + // both-note@#base {{no viable 'operator==' for base class 'D2'}} + // both-note@#base {{three-way comparison cannot be synthesized because there is no viable function for '==' comparison}} + // both-note@#base {{deleted comparison function for base class 'E'}} + // both-note@#base {{implied comparison for base class 'F' is ambiguous}} + template struct Cmp : T { // #base + std::strong_ordering operator<=>(const Cmp&) const = default; // #cmp both-note 5{{here}} + }; + + void use(...); + void f() { + use( + Cmp() <=> Cmp(), + Cmp() <=> Cmp(), + Cmp() <=> Cmp(), // both-error {{deleted}} + Cmp() <=> Cmp(), // both-error {{deleted}} + Cmp() <=> Cmp(), // both-error {{deleted}} + Cmp() <=> Cmp(), // both-error {{deleted}} + Cmp() <=> Cmp(), // both-error {{deleted}} + // FIXME: The following three errors are not very good. + // both-error@#cmp {{value of type 'void' is not contextually convertible to 'bool'}} + Cmp() <=> Cmp(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G1>' first required here}}j + // both-error@#cmp {{value of type 'void' is not contextually convertible to 'bool'}} + Cmp() <=> Cmp(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G2>' first required here}}j + // both-error@#cmp {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}} + Cmp() <=> Cmp(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}H>' first required here}}j + 0 + ); + } + + // both-note@#arr {{deleted comparison function for member 'arr'}} + // both-note@#arr {{no viable three-way comparison function for member 'arr'}} + // both-note@#arr {{three-way comparison cannot be synthesized because there is no viable function for '<' comparison}} + // both-note@#arr {{no viable 'operator==' for member 'arr'}} + // both-note@#arr {{three-way comparison cannot be synthesized because there is no viable function for '==' comparison}} + // both-note@#arr {{deleted comparison function for member 'arr'}} + // both-note@#arr {{implied comparison for member 'arr' is ambiguous}} + template struct CmpArray { + T arr[3]; // #arr + std::strong_ordering operator<=>(const CmpArray&) const = default; // #cmparray both-note 5{{here}} + }; + void g() { + use( + CmpArray() <=> CmpArray(), + CmpArray() <=> CmpArray(), + CmpArray() <=> CmpArray(), // both-error {{deleted}} + CmpArray() <=> CmpArray(), // both-error {{deleted}} + CmpArray() <=> CmpArray(), // both-error {{deleted}} + CmpArray() <=> CmpArray(), // both-error {{deleted}} + CmpArray() <=> CmpArray(), // both-error {{deleted}} + // FIXME: The following three errors are not very good. + // both-error@#cmparray {{value of type 'void' is not contextually convertible to 'bool'}} + CmpArray() <=> CmpArray(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G1>' first required here}}j + // both-error@#cmparray {{value of type 'void' is not contextually convertible to 'bool'}} + CmpArray() <=> CmpArray(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G2>' first required here}}j + // both-error@#cmparray {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}} + CmpArray() <=> CmpArray(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}H>' first required here}}j + 0 + ); + } +} + +namespace Access { + class A { + std::strong_ordering operator<=>(const A &) const; // both-note {{here}} + public: + bool operator==(const A &) const; + bool operator<(const A &) const; + }; + struct B { + A a; // both-note {{would invoke a private 'operator<=>'}} + friend std::strong_ordering operator<=>(const B &, const B &) = default; // both-warning {{deleted}} both-note{{replace 'default'}} + }; + + class C { + std::strong_ordering operator<=>(const C &); // not viable (not const) + bool operator==(const C &) const; // both-note {{here}} + bool operator<(const C &) const; + }; + struct D { + C c; // both-note {{would invoke a private 'operator=='}} + friend std::strong_ordering operator<=>(const D &, const D &) = default; // both-warning {{deleted}} both-note{{replace 'default'}} + }; +} + +namespace Synthesis { + enum Result { False, True, Mu }; + + constexpr bool toBool(Result R) { + if (R == Mu) throw "should not ask this question"; + return R == True; + } + + struct Val { + Result equal, less; + constexpr bool operator==(const Val&) const { return toBool(equal); } + constexpr bool operator<(const Val&) const { return toBool(less); } + }; + + template struct Cmp { + Val val; + friend T operator<=>(const Cmp&, const Cmp&) = default; // both-note {{deleted}} + }; + + template constexpr auto cmp(Result equal, Result less = Mu, Result reverse_less = Mu) { + return Cmp{equal, less} <=> Cmp{Mu, reverse_less}; + } + + static_assert(cmp(True) == 0); + static_assert(cmp(False, True) < 0); + static_assert(cmp(False, False) > 0); + + static_assert(cmp(True) == 0); + static_assert(cmp(False, True) < 0); + static_assert(cmp(False, False) > 0); + + static_assert(cmp(True) == 0); + static_assert(cmp(False, True) < 0); + static_assert(cmp(False, False, True) > 0); + static_assert(!(cmp(False, False, False) > 0)); + static_assert(!(cmp(False, False, False) == 0)); + static_assert(!(cmp(False, False, False) < 0)); + + // No synthesis is performed for a custom return type, even if it can be + // converted from a standard ordering. + struct custom_ordering { + custom_ordering(std::strong_ordering o); + }; + void f(Cmp c) { + c <=> c; // both-error {{deleted}} + } +} + +namespace Preference { + struct A { + A(const A&) = delete; // both-note {{deleted}} + // "usable" candidate that can't actually be called + friend void operator<=>(A, A); // both-note {{passing}} + // Callable candidates for synthesis not considered. + friend bool operator==(A, A); + friend bool operator<(A, A); + }; + + struct B { + B(); + A a; + std::strong_ordering operator<=>(const B&) const = default; // both-error {{call to deleted constructor of 'A'}} + }; + bool x = B() < B(); // both-note {{in defaulted three-way comparison operator for 'B' first required here}} +}