cpp_define (pfile, "__cpp_multidimensional_subscript=202110L");
cpp_define (pfile, "__cpp_named_character_escapes=202207L");
cpp_define (pfile, "__cpp_static_call_operator=202207L");
+ cpp_define (pfile, "__cpp_implicit_move=202207L");
}
if (flag_concepts)
{
/* Nor the reverse. */
if (!is_lvalue && !TYPE_REF_IS_RVALUE (rto)
- /* Unless it's really an lvalue. */
- && !(cxx_dialect >= cxx20
+ /* Unless it's really a C++20 lvalue being treated as an xvalue.
+ But in C++23, such an expression is just an xvalue, not a special
+ lvalue, so the binding is once again ill-formed. */
+ && !(cxx_dialect == cxx20
&& (gl_kind & clk_implicit_rval))
&& (!CP_TYPE_CONST_NON_VOLATILE_P (to)
|| (flags & LOOKUP_NO_RVAL_BIND))
extern tree make_template_placeholder (tree);
extern bool template_placeholder_p (tree);
extern bool ctad_template_p (tree);
+extern bool unparenthesized_id_or_class_member_access_p (tree);
extern tree do_auto_deduction (tree, tree, tree,
tsubst_flags_t
= tf_warning_or_error,
cp_type_quals (ptype));
}
+/* Return true if INIT is an unparenthesized id-expression or an
+ unparenthesized class member access. Used for the argument of
+ decltype(auto). */
+
+bool
+unparenthesized_id_or_class_member_access_p (tree init)
+{
+ STRIP_ANY_LOCATION_WRAPPER (init);
+
+ /* We need to be able to tell '(r)' and 'r' apart (when it's of
+ reference type). Only the latter is an id-expression. */
+ if (REFERENCE_REF_P (init)
+ && !REF_PARENTHESIZED_P (init))
+ init = TREE_OPERAND (init, 0);
+ return (DECL_P (init)
+ || ((TREE_CODE (init) == COMPONENT_REF
+ || TREE_CODE (init) == SCOPE_REF)
+ && !REF_PARENTHESIZED_P (init)));
+}
+
/* Replace occurrences of 'auto' in TYPE with the appropriate type deduced
from INIT. AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE.
The CONTEXT determines the context in which auto deduction is performed
auto_node. */
complain &= ~tf_partial;
+ /* In C++23, we must deduce the type to int&& for code like
+ decltype(auto) f(int&& x) { return (x); }
+ or
+ auto&& f(int x) { return x; }
+ so we use treat_lvalue_as_rvalue_p. But don't do it for
+ decltype(auto) f(int x) { return x; }
+ where we should deduce 'int' rather than 'int&&'; transmogrifying
+ INIT to an rvalue would break that. */
+ tree r;
+ if (cxx_dialect >= cxx23
+ && context == adc_return_type
+ && (!AUTO_IS_DECLTYPE (auto_node)
+ || !unparenthesized_id_or_class_member_access_p (init))
+ && (r = treat_lvalue_as_rvalue_p (maybe_undo_parenthesized_ref (init),
+ /*return*/true)))
+ init = r;
+
if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (auto_node))
/* C++17 class template argument deduction. */
return do_class_deduction (type, tmpl, init, flags, complain);
}
else if (AUTO_IS_DECLTYPE (auto_node))
{
- /* Figure out if INIT is an unparenthesized id-expression or an
- unparenthesized class member access. */
- tree stripped_init = tree_strip_any_location_wrapper (init);
- /* We need to be able to tell '(r)' and 'r' apart (when it's of
- reference type). Only the latter is an id-expression. */
- if (REFERENCE_REF_P (stripped_init)
- && !REF_PARENTHESIZED_P (stripped_init))
- stripped_init = TREE_OPERAND (stripped_init, 0);
- const bool id = (DECL_P (stripped_init)
- || ((TREE_CODE (stripped_init) == COMPONENT_REF
- || TREE_CODE (stripped_init) == SCOPE_REF)
- && !REF_PARENTHESIZED_P (stripped_init)));
+ const bool id = unparenthesized_id_or_class_member_access_p (init);
tree deduced = finish_decltype_type (init, id, complain);
deduced = canonicalize_type_argument (deduced, complain);
if (deduced == error_mark_node)
bool
xvalue_p (const_tree ref)
{
- return (lvalue_kind (ref) == clk_rvalueref);
+ return (lvalue_kind (ref) & clk_rvalueref);
}
/* True if REF is a bit-field. */
the conditions for the named return value optimization. */
bool converted = false;
tree moved;
- /* This is only interesting for class type. */
- if (CLASS_TYPE_P (functype)
- && (moved = treat_lvalue_as_rvalue_p (retval, /*return*/true)))
+ /* Until C++23, this was only interesting for class type, but in C++23,
+ we should do the below when we're converting rom/to a class/reference
+ (a non-scalar type). */
+ if ((cxx_dialect < cxx23
+ ? CLASS_TYPE_P (functype)
+ : !SCALAR_TYPE_P (functype) || !SCALAR_TYPE_P (TREE_TYPE (retval)))
+ && (moved = treat_lvalue_as_rvalue_p (retval, /*return*/true)))
{
if (cxx_dialect < cxx20)
{
A<float> g1()
{
float f[] = {1.1f, 2.3f};
- return f;
+ return f; // { dg-error "cannot bind non-const" "" { target c++23 } }
}
const A<float> &g3()
{
float f[] = {1.1f, 2.3f};
- return f; // { dg-warning "returning reference to temporary" }
+ return f; // { dg-warning "returning reference to temporary" "" { target c++20_down } }
+// { dg-error "non-const lvalue|invalid user-defined conversion" "" { target c++23 } .-1 }
}
A<float> &g4()
{
float f[] = {1.1f, 2.3f};
- return f; // { dg-error "cannot bind non-const lvalue ref" }
+ return f; // { dg-error "cannot bind non-const lvalue ref|invalid user-defined conversion" }
}
struct B
B g2()
{
int c[10];
- return c;
+ return c; // { dg-error "non-const lvalue" "" { target c++23 } }
}
-
S f()
{
S s;
- return s;
+ return s; // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } }
}
void g()
{
S s;
- throw s;
+ throw s; // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } }
}
// PR c++/91212
// Test that C++11 implicit move semantics don't call the const copy.
-// { dg-do link }
+// In C++23, we call #2.
+// { dg-do link { target c++20_down } }
struct T { int i; };
int main() {
int t;
int x{3};
- decltype (RtoL1(x+0)) y = t;
+ decltype (RtoL1(x+0)) y = t; // { dg-error "cannot bind rvalue reference" "" { target c++23 } }
}
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++14 } }
+// A variant of cxx23/elision1.C:eight, just with ().
+
+struct Widget {
+ Widget(Widget&&);
+};
+
+Widget val();
+
+decltype(auto)
+foo ()
+{
+ decltype(auto) x = val(); // OK, x is Widget
+ // We deduce the return type to int&&, therefore we're doing something
+ // we ought not to be doing -- returning a reference to a local variable!
+ // In C++20, we deduce to int&, but that has the same problem!
+ return (x); // { dg-warning "reference to local variable" }
+}
// { dg-do compile { target c++14 } }
struct A;
-struct B {
+struct B { // { dg-error "cannot bind" "" { target c++23 } }
struct C { C (); C (C &); } b;
};
struct D { A operator* (); };
auto bar () { return e; }
D e;
};
-struct F { B f; int g; };
+struct F { B f; int g; }; // { dg-error "use of deleted function" "" { target c++23 } }
int
main ()
{
E e;
auto f = *e.bar ();
- auto i = [&] { F g; g.g = 1; auto h = [&](auto) { g.g = 0; }; f.foo (h); return g; };
+ auto i = [&] { F g; g.g = 1; auto h = [&](auto) { g.g = 0; }; f.foo (h); return g; }; // { dg-error "use of deleted function" "" { target c++23 } }
}
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+// Tests from P2266R1, decltype-related changes in
+// $ 3.2.1. Interaction with decltype and decltype(auto)
+
+template<typename T, typename U>
+struct same_type { static const bool value = false; };
+
+template<typename T>
+struct same_type<T, T> { static const bool value = true; };
+
+auto f1(int x) -> decltype(x) { return (x); }
+static_assert(same_type<decltype(f1), int (int)>::value);
+auto f2(int x) -> decltype((x)) { return (x); } // { dg-error "cannot bind" }
+static_assert(same_type<decltype(f2), int& (int)>::value);
+auto f3(int x) -> decltype(auto) { return (x); } // { dg-warning "reference to local variable" }
+static_assert(same_type<decltype(f3), int&& (int)>::value);
+auto g1(int x) -> decltype(x) { return x; }
+static_assert(same_type<decltype(g1), int (int)>::value);
+auto g2(int x) -> decltype((x)) { return x; } // { dg-error "cannot bind" }
+static_assert(same_type<decltype(g2), int& (int)>::value);
+auto g3(int x) -> decltype(auto) { return x; }
+static_assert(same_type<decltype(g3), int (int)>::value);
+
+// Note that f2 and g2 are well-formed in C++20, but we propose to make
+// f2 and g2 ill-formed, because they attempt to bind an lvalue reference
+// to a move-eligible xvalue expression.
+
+struct X { };
+
+auto
+f4 (X x)
+{
+ return x;
+}
+static_assert(same_type<decltype(f4), X(X)>::value);
+
+auto&
+f5 (X x)
+{
+ return x; // { dg-error "cannot bind non-const lvalue reference" }
+}
+static_assert(same_type<decltype(f5), X&(X)>::value);
+
+auto&&
+f6 (X x)
+{
+ return x; // { dg-warning "reference to local variable" }
+}
+static_assert(same_type<decltype(f6), X&&(X)>::value);
+
+auto
+f7 (X x)
+{
+ return (x);
+}
+static_assert(same_type<decltype(f7), X(X)>::value);
+
+auto&
+f8 (X x)
+{
+ return (x); // { dg-error "cannot bind non-const lvalue reference" }
+}
+static_assert(same_type<decltype(f8), X&(X)>::value);
+
+auto&&
+f9 (X x)
+{
+ return (x); // { dg-warning "reference to local variable" }
+}
+static_assert(same_type<decltype(f9), X&&(X)>::value);
+
+decltype(auto)
+f10 (X x)
+{
+ return x;
+}
+static_assert(same_type<decltype(f10), X(X)>::value);
+
+decltype(auto)
+f11 (X x)
+{
+ return (x); // { dg-warning "reference to local variable" }
+}
+static_assert(same_type<decltype(f11), X&&(X)>::value);
+
+decltype(auto)
+f12 (X& x)
+{
+ return x;
+}
+static_assert(same_type<decltype(f12), X&(X&)>::value);
+
+decltype(auto)
+f13 (X& x)
+{
+ return (x);
+}
+static_assert(same_type<decltype(f13), X&(X&)>::value);
+
+decltype(auto)
+f14 (X&& x)
+{
+ return x;
+}
+static_assert(same_type<decltype(f14), X&&(X&&)>::value);
+
+decltype(auto)
+f15 (X&& x)
+{
+ return (x);
+}
+static_assert(same_type<decltype(f15), X&&(X&&)>::value);
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+// Test decltype(auto) more.
+
+template<typename T, typename U>
+struct same_type { static const bool value = false; };
+
+template<typename T>
+struct same_type<T, T> { static const bool value = true; };
+
+struct Widget {
+ int x;
+};
+
+Widget wg;
+
+decltype(auto) fn0(Widget&& x) {
+ return (::wg);
+}
+static_assert(same_type<decltype(fn0), Widget& (Widget&&)>::value);
+
+decltype(auto) fn1(Widget&& x) {
+ return ::wg;
+}
+static_assert(same_type<decltype(fn1), Widget (Widget&&)>::value);
+
+decltype(auto) fn2() {
+ Widget w;
+ return w;
+}
+static_assert(same_type<decltype(fn2), Widget ()>::value);
+
+decltype(auto) fn3() {
+ Widget w;
+ return (w); // { dg-warning "reference to local variable" }
+}
+static_assert(same_type<decltype(fn3), Widget&& ()>::value);
+
+decltype(auto) fn4() {
+ Widget w;
+ return w.x;
+}
+static_assert(same_type<decltype(fn4), int ()>::value);
+
+decltype(auto) fn5() {
+ Widget w;
+ return (w.x); // { dg-warning "reference to local variable" }
+}
+static_assert(same_type<decltype(fn5), int& ()>::value);
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+// Tests from P2266R1.
+
+namespace std {
+ template<typename _Tp>
+ struct remove_reference
+ { typedef _Tp type; };
+
+ template<typename _Tp>
+ struct remove_reference<_Tp&>
+ { typedef _Tp type; };
+
+ template<typename _Tp>
+ struct remove_reference<_Tp&&>
+ { typedef _Tp type; };
+
+ template<typename _Tp>
+ constexpr typename std::remove_reference<_Tp>::type&&
+ move(_Tp&& __t) noexcept
+ { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
+}
+
+template<typename T, typename U>
+struct same_type { static const bool value = false; };
+
+template<typename T>
+struct same_type<T, T> { static const bool value = true; };
+
+struct Widget {
+ Widget(Widget&&);
+};
+
+struct RRefTaker {
+ RRefTaker(Widget&&);
+};
+
+struct Mutt {
+ operator int*() &&;
+};
+
+struct Jeff {
+ operator int&() &&;
+};
+
+struct Ella {
+ operator int() &&;
+};
+
+Widget one(Widget w) {
+ return w; // OK since C++11
+}
+
+RRefTaker two(Widget w) {
+ return w; // OK since C++11 + CWG1579
+}
+
+RRefTaker three(Widget&& w) {
+ return w; // OK since C++20 because P0527
+}
+
+// Tests that implicit move applies even to functions that return references.
+Widget&& four(Widget&& w) {
+ return w; // OK since C++23
+}
+
+// ... or pointers.
+int* five(Mutt x) {
+ return x; // OK since C++20 because P1155
+}
+
+int& six(Jeff x) {
+ return x;
+}
+
+int test_ella(Ella e) {
+ return e;
+}
+
+template<class T>
+T&& seven(T&& x) { return x; }
+
+void test_seven(Widget w) {
+ Widget& r = seven(w);
+ Widget&& rr = seven(std::move(w));
+}
+
+Widget val();
+Widget& lref();
+Widget&& rref();
+
+decltype(auto) eight() {
+ decltype(auto) x = val(); // OK, x is Widget
+ return x; // OK, return type is Widget, we get copy elision
+}
+
+decltype(auto) nine() {
+ decltype(auto) x = lref(); // OK, x is Widget&
+ return x; // OK, return type is Widget&
+}
+
+decltype(auto) ten() {
+ decltype(auto) x = rref(); // OK, x is Widget&&
+ // This was an error: return type is Widget&&, cannot bind to x.
+ // But in C++23, x is treated as an rvalue.
+ return x;
+}
+
+// Now returns Widget&&, not Widget&.
+// This is from $ 3.2.1. Interaction with decltype and decltype(auto).
+decltype(auto) eleven(Widget&& x) {
+ return (x);
+}
+static_assert(same_type<decltype(eleven), Widget&& (Widget&&)>::value);
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++20 } }
+// Test from P2266R1, $ 3.3. Two overload resolutions are overly confusing.
+
+struct Widget {
+ Widget();
+ Widget(Widget&&);
+};
+
+struct Frodo {
+ Frodo(Widget&);
+ Frodo(Widget&&) = delete;
+};
+
+struct Sam {
+ Sam(Widget&) = delete; // #1
+ Sam(const Widget&); // #2
+};
+
+Sam twelve() {
+ Widget w;
+ // This is supposed to call #2 since C++20 because P1155.
+ // But we actually choose #1 since r11-2411 (in C++20 only).
+ return w; // { dg-error "deleted" "" { target c++20_only } }
+}
+
+Frodo thirteen() {
+ Widget w;
+ // This is a correct error in both C++20 and C++23.
+ return w; // { dg-error "use of deleted function" }
+}
+
+struct Merry {};
+struct Pippin {};
+struct Together : Merry, Pippin {};
+struct Quest {
+ Quest(Merry&&);
+ Quest(Pippin&&);
+ Quest(Together&);
+};
+
+Quest fourteen() {
+ Together t;
+ // C++20: calls Quest(Together&). Proposed: ill-formed.
+ return t; // { dg-error "ambiguous" "" { target c++23 } }
+}
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+// Test from P2266R1, $ 3.4. A specific case involving reference_wrapper.
+
+#include <functional>
+
+struct Widget {
+ Widget();
+ Widget(Widget&&);
+};
+
+std::reference_wrapper<Widget> fifteen() {
+ Widget w;
+ // OK until CWG1579; OK after LWG2993. Proposed: ill-formed
+ return w; // { dg-error "could not convert" }
+}
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+// Test from P2266R1, $ 5.2. LibreOffice OString constructor.
+
+struct X {
+ X(auto&);
+};
+
+// The following compiles in C++20 (deducing X(char (&)[10])) but not
+// after P2266 (because the returned expression now has type char (&&)[10],
+// which cannot bind to auto&).
+X f() {
+ char a[10];
+ return a; // { dg-error "cannot bind non-const lvalue reference" }
+}
+
+// The solution was to change it by making the return convert explicitly
+// rather than implicitly:
+X fixed() {
+ char a[10];
+ return X(a);
+}
+
+// $ 5.3. LibreOffice o3tl::temporary
+
+template<class T>
+T& temporary1(T&& x) { return x; } // { dg-error "cannot bind non-const lvalue reference" }
+
+// Fixed by:
+template<class T>
+T& temporary2(T&& x) { return static_cast<T&>(x); }
+
+void
+test ()
+{
+ int& r1 = temporary1 (42);
+ int& r2 = temporary2 (42);
+}
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+// Test from [class.copy.elision]/4.
+
+class Thing {
+public:
+ Thing();
+ ~Thing();
+ Thing(Thing&&);
+private:
+ Thing(const Thing&);
+};
+
+Thing f(bool b) {
+ Thing t;
+ if (b)
+ throw t; // OK, Thing(Thing&&) used (or elided) to throw t
+ return t; // OK, Thing(Thing&&) used (or elided) to return t
+}
+
+Thing t2 = f(false); // OK, no extra copy/move performed, t2 constructed by call to f
+
+struct Weird {
+ Weird();
+ Weird(Weird&);
+};
+
+Weird g(bool b) {
+ static Weird w1;
+ Weird w2;
+ if (b) {
+ return w1; // OK: Weird(Weird&)
+ } else {
+ return w2; // { dg-error "cannot bind non-const lvalue reference" }
+ }
+}
+
+int& h(bool b, int i) {
+ static int s;
+ if (b)
+ return s; // OK
+ else
+ return i; // { dg-error "cannot bind non-const lvalue reference" }
+}
+
+decltype(auto) h2(Thing t) {
+ return t; // OK, t is an xvalue and h2's return type is Thing
+}
+
+decltype(auto) h3(Thing t) {
+ // OK, (t) is an xvalue and h3's return type is Thing&&
+ return (t); // { dg-warning "reference to local variable" }
+}
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+// From [diff.cpp20.expr].
+
+template<typename T, typename U>
+struct same_type { static const bool value = false; };
+
+template<typename T>
+struct same_type<T, T> { static const bool value = true; };
+
+// In C++23, returns int&&; previously returned int&.
+decltype(auto) f(int&& x) { return (x); }
+static_assert(same_type<decltype(f), int&& (int&&)>::value);
+
+// This used to work in C++20.
+int& g(int&& x) { return x; } // { dg-error "cannot bind non-const lvalue reference" }
+
+template<typename T>
+decltype(auto) h(T&& x) { return (x); }
+static_assert(same_type<decltype(h(42)), int&&>::value);
--- /dev/null
+// PR c++/101165 - P2266R1 - Simpler implicit move
+// { dg-do compile { target c++23 } }
+
+struct X {
+ X ();
+ X(X&&);
+};
+
+X&& rref ();
+
+X&&
+f1 (X&& x)
+{
+ return x;
+}
+
+template<typename T> T&&
+f2 (T&& x)
+{
+ return x;
+}
+template X& f2<X&>(X&);
+template X&& f2<X>(X&&);
+
+X&&
+f3 ()
+{
+ X&& x = rref ();
+ return x;
+}
+
+void
+f4 ()
+try {
+ X x;
+ throw x;
+} catch (...) { }
+
+void
+f5 ()
+{
+ auto l1 = [](auto x) -> auto { return x; };
+ auto &&x1 = l1(X{});
+ auto l2 = [](auto x) -> auto& { return x; }; // { dg-error "cannot bind non-const lvalue reference" }
+ auto &&x2 = l2(X{});
+ auto l3 = [](auto x) -> auto&& { return x; }; // { dg-warning "reference to local" }
+ auto &&x3 = l3(X{});
+}
+
+constexpr int &
+f6 (int &&n)
+{
+ return n; // { dg-error "cannot bind non-const lvalue reference" }
+}
+
+void
+do_f6 ()
+{
+ auto x = f6 (42);
+}
+
+template<typename T> auto &
+f7 (T &&t)
+{
+ return t; // { dg-error "cannot bind non-const lvalue reference" }
+}
+
+void
+do_f7 ()
+{
+ const int &x = f7 (0);
+}
#elif __cpp_static_call_operator != 202207
# error "__cpp_static_call_operator != 202207"
#endif
+
+#ifndef __cpp_implicit_move
+# error "__cpp_implicit_move"
+#elif __cpp_implicit_move != 202207
+# error "__cpp_implicit_move != 202207"
+#endif
// PR middle-end/56217
-// { dg-do compile }
+// { dg-do compile { target c++20_down } }
// { dg-options "-fopenmp" }
struct S { int *p; S (); S (S &); };
S s;
#pragma omp task shared (s)
s.p = 0;
+ // This fails in C++23, because "cannot bind non-const lvalue reference of
+ // type 'S&' to an rvalue of type 'S'".
return s;
}
int& bad1()
{
int x = 0;
- return x;
+ return x; // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } }
}
int* bad2()
int& bad1()
{
int x = 0;
- return x; // { dg-error "reference to local variable" }
+ return x; // { dg-error "reference to local variable|cannot bind non-const lvalue reference" }
}
int* bad2()
local = x+2;
- return local; // { dg-warning "reference to local" }
+ return local; // { dg-warning "reference to local" "" { target c++20_down } }
+// { dg-error "non-const lvalue" "" { target c++23 } .-1 }
}
int i;
};
-X foo() { X x; return x; }
+X foo() { X x; return x; } // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } }
int main()
{
B A::compute(void) const
{
B sub(*this, 1);
- return sub;
+ return static_cast<B&>(sub);
}
int main ()