diff options
Diffstat (limited to 'gcc')
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 () |