aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/c-cppbuiltin.cc1
-rw-r--r--gcc/cp/call.cc6
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/pt.cc50
-rw-r--r--gcc/cp/tree.cc2
-rw-r--r--gcc/cp/typeck.cc10
-rw-r--r--gcc/testsuite/g++.dg/conversion/pr41426.C10
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/elision_weak.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/move-return3.C3
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C19
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C6
-rw-r--r--gcc/testsuite/g++.dg/cpp23/decltype1.C113
-rw-r--r--gcc/testsuite/g++.dg/cpp23/decltype2.C49
-rw-r--r--gcc/testsuite/g++.dg/cpp23/elision1.C114
-rw-r--r--gcc/testsuite/g++.dg/cpp23/elision2.C46
-rw-r--r--gcc/testsuite/g++.dg/cpp23/elision3.C16
-rw-r--r--gcc/testsuite/g++.dg/cpp23/elision4.C38
-rw-r--r--gcc/testsuite/g++.dg/cpp23/elision5.C53
-rw-r--r--gcc/testsuite/g++.dg/cpp23/elision6.C20
-rw-r--r--gcc/testsuite/g++.dg/cpp23/elision7.C72
-rw-r--r--gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C6
-rw-r--r--gcc/testsuite/g++.dg/gomp/pr56217.C4
-rw-r--r--gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C2
-rw-r--r--gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C2
-rw-r--r--gcc/testsuite/g++.old-deja/g++.brendan/crash55.C3
-rw-r--r--gcc/testsuite/g++.old-deja/g++.jason/temporary2.C2
-rw-r--r--gcc/testsuite/g++.old-deja/g++.mike/p2846b.C2
28 files changed, 620 insertions, 36 deletions
diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index ca5f500..d4de5a0 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1082,6 +1082,7 @@ c_cpp_builtins (cpp_reader *pfile)
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)
{
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index fc86b74..3506b0f 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -1880,8 +1880,10 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
/* 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))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 99b486b8..19bbfbc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7292,6 +7292,7 @@ extern tree make_constrained_decltype_auto (tree, tree);
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,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 1c1e573..2d83dfd 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -30408,6 +30408,26 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
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
@@ -30443,6 +30463,23 @@ do_auto_deduction (tree type, tree init, tree auto_node,
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);
@@ -30504,18 +30541,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
}
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)
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index d0bd41a..ea4dfc6 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -382,7 +382,7 @@ obvalue_p (const_tree ref)
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. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 4854b98..5f16c4d 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -11042,9 +11042,13 @@ check_return_expr (tree retval, bool *no_warning)
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)
{
diff --git a/gcc/testsuite/g++.dg/conversion/pr41426.C b/gcc/testsuite/g++.dg/conversion/pr41426.C
index 5493a91..b4ecbca 100644
--- a/gcc/testsuite/g++.dg/conversion/pr41426.C
+++ b/gcc/testsuite/g++.dg/conversion/pr41426.C
@@ -11,19 +11,20 @@ struct A
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
@@ -35,6 +36,5 @@ struct B
B g2()
{
int c[10];
- return c;
+ return c; // { dg-error "non-const lvalue" "" { target c++23 } }
}
-
diff --git a/gcc/testsuite/g++.dg/cpp0x/elision_weak.C b/gcc/testsuite/g++.dg/cpp0x/elision_weak.C
index e8ba755..ddd1274 100644
--- a/gcc/testsuite/g++.dg/cpp0x/elision_weak.C
+++ b/gcc/testsuite/g++.dg/cpp0x/elision_weak.C
@@ -9,11 +9,11 @@ struct S
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 } }
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/move-return3.C b/gcc/testsuite/g++.dg/cpp0x/move-return3.C
index c79f059..30a936f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/move-return3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/move-return3.C
@@ -1,6 +1,7 @@
// 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; };
diff --git a/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C b/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C
index 56e011e..24b32ed 100644
--- a/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C
+++ b/gcc/testsuite/g++.dg/cpp1y/decltype-auto2.C
@@ -8,5 +8,5 @@ auto constexpr RtoL1(T&& r) -> decltype(auto) {
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 } }
}
diff --git a/gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C b/gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C
new file mode 100644
index 0000000..da53278
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/decltype-auto6.C
@@ -0,0 +1,19 @@
+// 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" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C
index 46ce909..8e64d4e 100644
--- a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-89419.C
@@ -2,7 +2,7 @@
// { 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* (); };
@@ -13,12 +13,12 @@ struct E {
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 } }
}
diff --git a/gcc/testsuite/g++.dg/cpp23/decltype1.C b/gcc/testsuite/g++.dg/cpp23/decltype1.C
new file mode 100644
index 0000000..6f3cd0d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/decltype1.C
@@ -0,0 +1,113 @@
+// 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);
diff --git a/gcc/testsuite/g++.dg/cpp23/decltype2.C b/gcc/testsuite/g++.dg/cpp23/decltype2.C
new file mode 100644
index 0000000..84679c4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/decltype2.C
@@ -0,0 +1,49 @@
+// 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);
diff --git a/gcc/testsuite/g++.dg/cpp23/elision1.C b/gcc/testsuite/g++.dg/cpp23/elision1.C
new file mode 100644
index 0000000..f44fd2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision1.C
@@ -0,0 +1,114 @@
+// 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);
diff --git a/gcc/testsuite/g++.dg/cpp23/elision2.C b/gcc/testsuite/g++.dg/cpp23/elision2.C
new file mode 100644
index 0000000..a698fc9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision2.C
@@ -0,0 +1,46 @@
+// 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 } }
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/elision3.C b/gcc/testsuite/g++.dg/cpp23/elision3.C
new file mode 100644
index 0000000..246342e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision3.C
@@ -0,0 +1,16 @@
+// 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" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/elision4.C b/gcc/testsuite/g++.dg/cpp23/elision4.C
new file mode 100644
index 0000000..c19b86b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision4.C
@@ -0,0 +1,38 @@
+// 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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/elision5.C b/gcc/testsuite/g++.dg/cpp23/elision5.C
new file mode 100644
index 0000000..a7d3e7c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision5.C
@@ -0,0 +1,53 @@
+// 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" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/elision6.C b/gcc/testsuite/g++.dg/cpp23/elision6.C
new file mode 100644
index 0000000..5d58da9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision6.C
@@ -0,0 +1,20 @@
+// 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);
diff --git a/gcc/testsuite/g++.dg/cpp23/elision7.C b/gcc/testsuite/g++.dg/cpp23/elision7.C
new file mode 100644
index 0000000..19fa89a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/elision7.C
@@ -0,0 +1,72 @@
+// 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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index 2f6b21ead7..b52cf37 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -569,3 +569,9 @@
#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
diff --git a/gcc/testsuite/g++.dg/gomp/pr56217.C b/gcc/testsuite/g++.dg/gomp/pr56217.C
index 03dfc5f..731c0c0 100644
--- a/gcc/testsuite/g++.dg/gomp/pr56217.C
+++ b/gcc/testsuite/g++.dg/gomp/pr56217.C
@@ -1,5 +1,5 @@
// PR middle-end/56217
-// { dg-do compile }
+// { dg-do compile { target c++20_down } }
// { dg-options "-fopenmp" }
struct S { int *p; S (); S (S &); };
@@ -10,5 +10,7 @@ foo ()
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;
}
diff --git a/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C b/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C
index e15bfa2..cc9bb59 100644
--- a/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C
+++ b/gcc/testsuite/g++.dg/warn/Wno-return-local-addr.C
@@ -4,7 +4,7 @@
int& bad1()
{
int x = 0;
- return x;
+ return x; // { dg-error "cannot bind non-const lvalue reference" "" { target c++23 } }
}
int* bad2()
diff --git a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C
index 642a576..4c18c2f 100644
--- a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C
+++ b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr.C
@@ -5,7 +5,7 @@
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()
diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C b/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C
index fd4d4b6..b93e6e0 100644
--- a/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C
+++ b/gcc/testsuite/g++.old-deja/g++.brendan/crash55.C
@@ -8,5 +8,6 @@
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 }
}
diff --git a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C
index c855f8f..2709b50 100644
--- a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C
+++ b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C
@@ -8,7 +8,7 @@ public:
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()
{
diff --git a/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C b/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C
index 57422fe..5bcf9e3 100644
--- a/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C
+++ b/gcc/testsuite/g++.old-deja/g++.mike/p2846b.C
@@ -42,7 +42,7 @@ public:
B A::compute(void) const
{
B sub(*this, 1);
- return sub;
+ return static_cast<B&>(sub);
}
int main ()