Skip to content

Commit b70f42a

Browse files
committed
[clang][Interp] Handle PseudoObjectExprs
Evaluate all the semantic expressions.
1 parent 4cba595 commit b70f42a

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,31 @@ bool ByteCodeExprGen<Emitter>::VisitCXXRewrittenBinaryOperator(
21892189
return this->delegate(E->getSemanticForm());
21902190
}
21912191

2192+
template <class Emitter>
2193+
bool ByteCodeExprGen<Emitter>::VisitPseudoObjectExpr(
2194+
const PseudoObjectExpr *E) {
2195+
2196+
for (const Expr *SemE : E->semantics()) {
2197+
if (auto *OVE = dyn_cast<OpaqueValueExpr>(SemE)) {
2198+
if (SemE == E->getResultExpr())
2199+
return false;
2200+
2201+
if (OVE->isUnique())
2202+
continue;
2203+
2204+
if (!this->discard(OVE))
2205+
return false;
2206+
} else if (SemE == E->getResultExpr()) {
2207+
if (!this->delegate(SemE))
2208+
return false;
2209+
} else {
2210+
if (!this->discard(SemE))
2211+
return false;
2212+
}
2213+
}
2214+
return true;
2215+
}
2216+
21922217
template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
21932218
if (E->containsErrors())
21942219
return false;

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
117117
bool VisitRequiresExpr(const RequiresExpr *E);
118118
bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
119119
bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *E);
120+
bool VisitPseudoObjectExpr(const PseudoObjectExpr *E);
120121

121122
protected:
122123
bool visitExpr(const Expr *E) override;

clang/test/AST/Interp/spaceship.cpp

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// RUN: %clang_cc1 -std=c++2a -verify=both,ref %s -fcxx-exceptions
2+
// RUN: %clang_cc1 -std=c++2a -verify=both,expected %s -fcxx-exceptions -fexperimental-new-constant-interpreter
3+
4+
namespace std {
5+
struct strong_ordering { // both-note 6{{candidate}}
6+
int n;
7+
constexpr operator int() const { return n; }
8+
static const strong_ordering less, equal, greater;
9+
};
10+
constexpr strong_ordering strong_ordering::less{-1},
11+
strong_ordering::equal{0}, strong_ordering::greater{1};
12+
13+
struct weak_ordering {
14+
int n;
15+
constexpr weak_ordering(int n) : n(n) {}
16+
constexpr weak_ordering(strong_ordering o) : n(o.n) {}
17+
constexpr operator int() const { return n; }
18+
static const weak_ordering less, equivalent, greater;
19+
};
20+
constexpr weak_ordering weak_ordering::less{-1},
21+
weak_ordering::equivalent{0}, weak_ordering::greater{1};
22+
23+
struct partial_ordering {
24+
double d;
25+
constexpr partial_ordering(double d) : d(d) {}
26+
constexpr partial_ordering(strong_ordering o) : d(o.n) {}
27+
constexpr partial_ordering(weak_ordering o) : d(o.n) {}
28+
constexpr operator double() const { return d; }
29+
static const partial_ordering less, equivalent, greater, unordered;
30+
};
31+
constexpr partial_ordering partial_ordering::less{-1},
32+
partial_ordering::equivalent{0}, partial_ordering::greater{1},
33+
partial_ordering::unordered{__builtin_nan("")};
34+
35+
static_assert(!(partial_ordering::unordered < 0));
36+
static_assert(!(partial_ordering::unordered == 0));
37+
static_assert(!(partial_ordering::unordered > 0));
38+
}
39+
40+
namespace Deletedness {
41+
struct A {
42+
std::strong_ordering operator<=>(const A&) const;
43+
};
44+
struct B {
45+
bool operator==(const B&) const;
46+
bool operator<(const B&) const;
47+
};
48+
struct C {
49+
std::strong_ordering operator<=>(const C&) const = delete; // both-note 2{{deleted}}
50+
};
51+
struct D1 {
52+
bool operator==(const D1&) const;
53+
std::strong_ordering operator<=>(int) const; // both-note 2{{function not viable}} both-note 2{{function (with reversed parameter order) not viable}}
54+
bool operator<(int) const; // both-note 2{{function not viable}}
55+
};
56+
struct D2 {
57+
bool operator<(const D2&) const;
58+
std::strong_ordering operator<=>(int) const; // both-note 2{{function not viable}} both-note 2{{function (with reversed parameter order) not viable}}
59+
bool operator==(int) const; // both-note 2{{function not viable}}
60+
};
61+
struct E {
62+
bool operator==(const E&) const;
63+
bool operator<(const E&) const = delete; // both-note 2{{deleted}}
64+
};
65+
struct F {
66+
std::strong_ordering operator<=>(const F&) const; // both-note 2{{candidate}}
67+
std::strong_ordering operator<=>(F) const; // both-note 2{{candidate}}
68+
};
69+
struct G1 {
70+
bool operator==(const G1&) const;
71+
void operator<(const G1&) const;
72+
};
73+
struct G2 {
74+
void operator==(const G2&) const;
75+
bool operator<(const G2&) const;
76+
};
77+
struct H {
78+
void operator<=>(const H&) const;
79+
};
80+
81+
// both-note@#base {{deleted comparison function for base class 'C'}}
82+
// both-note@#base {{no viable three-way comparison function for base class 'D1'}}
83+
// both-note@#base {{three-way comparison cannot be synthesized because there is no viable function for '<' comparison}}
84+
// both-note@#base {{no viable 'operator==' for base class 'D2'}}
85+
// both-note@#base {{three-way comparison cannot be synthesized because there is no viable function for '==' comparison}}
86+
// both-note@#base {{deleted comparison function for base class 'E'}}
87+
// both-note@#base {{implied comparison for base class 'F' is ambiguous}}
88+
template<typename T> struct Cmp : T { // #base
89+
std::strong_ordering operator<=>(const Cmp&) const = default; // #cmp both-note 5{{here}}
90+
};
91+
92+
void use(...);
93+
void f() {
94+
use(
95+
Cmp<A>() <=> Cmp<A>(),
96+
Cmp<B>() <=> Cmp<B>(),
97+
Cmp<C>() <=> Cmp<C>(), // both-error {{deleted}}
98+
Cmp<D1>() <=> Cmp<D1>(), // both-error {{deleted}}
99+
Cmp<D2>() <=> Cmp<D2>(), // both-error {{deleted}}
100+
Cmp<E>() <=> Cmp<E>(), // both-error {{deleted}}
101+
Cmp<F>() <=> Cmp<F>(), // both-error {{deleted}}
102+
// FIXME: The following three errors are not very good.
103+
// both-error@#cmp {{value of type 'void' is not contextually convertible to 'bool'}}
104+
Cmp<G1>() <=> Cmp<G1>(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G1>' first required here}}j
105+
// both-error@#cmp {{value of type 'void' is not contextually convertible to 'bool'}}
106+
Cmp<G2>() <=> Cmp<G2>(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G2>' first required here}}j
107+
// both-error@#cmp {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}}
108+
Cmp<H>() <=> Cmp<H>(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}H>' first required here}}j
109+
0
110+
);
111+
}
112+
113+
// both-note@#arr {{deleted comparison function for member 'arr'}}
114+
// both-note@#arr {{no viable three-way comparison function for member 'arr'}}
115+
// both-note@#arr {{three-way comparison cannot be synthesized because there is no viable function for '<' comparison}}
116+
// both-note@#arr {{no viable 'operator==' for member 'arr'}}
117+
// both-note@#arr {{three-way comparison cannot be synthesized because there is no viable function for '==' comparison}}
118+
// both-note@#arr {{deleted comparison function for member 'arr'}}
119+
// both-note@#arr {{implied comparison for member 'arr' is ambiguous}}
120+
template<typename T> struct CmpArray {
121+
T arr[3]; // #arr
122+
std::strong_ordering operator<=>(const CmpArray&) const = default; // #cmparray both-note 5{{here}}
123+
};
124+
void g() {
125+
use(
126+
CmpArray<A>() <=> CmpArray<A>(),
127+
CmpArray<B>() <=> CmpArray<B>(),
128+
CmpArray<C>() <=> CmpArray<C>(), // both-error {{deleted}}
129+
CmpArray<D1>() <=> CmpArray<D1>(), // both-error {{deleted}}
130+
CmpArray<D2>() <=> CmpArray<D2>(), // both-error {{deleted}}
131+
CmpArray<E>() <=> CmpArray<E>(), // both-error {{deleted}}
132+
CmpArray<F>() <=> CmpArray<F>(), // both-error {{deleted}}
133+
// FIXME: The following three errors are not very good.
134+
// both-error@#cmparray {{value of type 'void' is not contextually convertible to 'bool'}}
135+
CmpArray<G1>() <=> CmpArray<G1>(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G1>' first required here}}j
136+
// both-error@#cmparray {{value of type 'void' is not contextually convertible to 'bool'}}
137+
CmpArray<G2>() <=> CmpArray<G2>(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G2>' first required here}}j
138+
// both-error@#cmparray {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}}
139+
CmpArray<H>() <=> CmpArray<H>(), // both-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}H>' first required here}}j
140+
0
141+
);
142+
}
143+
}
144+
145+
namespace Access {
146+
class A {
147+
std::strong_ordering operator<=>(const A &) const; // both-note {{here}}
148+
public:
149+
bool operator==(const A &) const;
150+
bool operator<(const A &) const;
151+
};
152+
struct B {
153+
A a; // both-note {{would invoke a private 'operator<=>'}}
154+
friend std::strong_ordering operator<=>(const B &, const B &) = default; // both-warning {{deleted}} both-note{{replace 'default'}}
155+
};
156+
157+
class C {
158+
std::strong_ordering operator<=>(const C &); // not viable (not const)
159+
bool operator==(const C &) const; // both-note {{here}}
160+
bool operator<(const C &) const;
161+
};
162+
struct D {
163+
C c; // both-note {{would invoke a private 'operator=='}}
164+
friend std::strong_ordering operator<=>(const D &, const D &) = default; // both-warning {{deleted}} both-note{{replace 'default'}}
165+
};
166+
}
167+
168+
namespace Synthesis {
169+
enum Result { False, True, Mu };
170+
171+
constexpr bool toBool(Result R) {
172+
if (R == Mu) throw "should not ask this question";
173+
return R == True;
174+
}
175+
176+
struct Val {
177+
Result equal, less;
178+
constexpr bool operator==(const Val&) const { return toBool(equal); }
179+
constexpr bool operator<(const Val&) const { return toBool(less); }
180+
};
181+
182+
template<typename T> struct Cmp {
183+
Val val;
184+
friend T operator<=>(const Cmp&, const Cmp&) = default; // both-note {{deleted}}
185+
};
186+
187+
template<typename T> constexpr auto cmp(Result equal, Result less = Mu, Result reverse_less = Mu) {
188+
return Cmp<T>{equal, less} <=> Cmp<T>{Mu, reverse_less};
189+
}
190+
191+
static_assert(cmp<std::strong_ordering>(True) == 0);
192+
static_assert(cmp<std::strong_ordering>(False, True) < 0);
193+
static_assert(cmp<std::strong_ordering>(False, False) > 0);
194+
195+
static_assert(cmp<std::weak_ordering>(True) == 0);
196+
static_assert(cmp<std::weak_ordering>(False, True) < 0);
197+
static_assert(cmp<std::weak_ordering>(False, False) > 0);
198+
199+
static_assert(cmp<std::partial_ordering>(True) == 0);
200+
static_assert(cmp<std::partial_ordering>(False, True) < 0);
201+
static_assert(cmp<std::partial_ordering>(False, False, True) > 0);
202+
static_assert(!(cmp<std::partial_ordering>(False, False, False) > 0));
203+
static_assert(!(cmp<std::partial_ordering>(False, False, False) == 0));
204+
static_assert(!(cmp<std::partial_ordering>(False, False, False) < 0));
205+
206+
// No synthesis is performed for a custom return type, even if it can be
207+
// converted from a standard ordering.
208+
struct custom_ordering {
209+
custom_ordering(std::strong_ordering o);
210+
};
211+
void f(Cmp<custom_ordering> c) {
212+
c <=> c; // both-error {{deleted}}
213+
}
214+
}
215+
216+
namespace Preference {
217+
struct A {
218+
A(const A&) = delete; // both-note {{deleted}}
219+
// "usable" candidate that can't actually be called
220+
friend void operator<=>(A, A); // both-note {{passing}}
221+
// Callable candidates for synthesis not considered.
222+
friend bool operator==(A, A);
223+
friend bool operator<(A, A);
224+
};
225+
226+
struct B {
227+
B();
228+
A a;
229+
std::strong_ordering operator<=>(const B&) const = default; // both-error {{call to deleted constructor of 'A'}}
230+
};
231+
bool x = B() < B(); // both-note {{in defaulted three-way comparison operator for 'B' first required here}}
232+
}

0 commit comments

Comments
 (0)