summaryrefslogtreecommitdiff
path: root/libstdc++-v3
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2022-06-29 19:00:54 -0400
committerMarek Polacek <polacek@redhat.com>2022-07-15 11:30:38 -0400
commit9a15d3beace26d68561cb3481b70b0bbcb122ca5 (patch)
tree7db269b8e6e93169326693418b3ac42c50c0d62d /libstdc++-v3
parent0a8edfbd37d399d1103d86e134ba0a92f8c873c3 (diff)
c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477]
This patch implements C++23 P2255R2, which adds two new type traits to detect reference binding to a temporary. They can be used to detect code like std::tuple<const std::string&> t("meow"); which is incorrect because it always creates a dangling reference, because the std::string temporary is created inside the selected constructor of std::tuple, and not outside it. There are two new compiler builtins, __reference_constructs_from_temporary and __reference_converts_from_temporary. The former is used to simulate direct- and the latter copy-initialization context. But I had a hard time finding a test where there's actually a difference. Under DR 2267, both of these are invalid: struct A { } a; struct B { explicit B(const A&); }; const B &b1{a}; const B &b2(a); so I had to peruse [over.match.ref], and eventually realized that the difference can be seen here: struct G { operator int(); // #1 explicit operator int&&(); // #2 }; int&& r1(G{}); // use #2 (no temporary) int&& r2 = G{}; // use #1 (a temporary is created to be bound to int&&) The implementation itself was rather straightforward because we already have the conv_binds_ref_to_prvalue function. The main function here is ref_xes_from_temporary. I've changed the return type of ref_conv_binds_directly to tristate, because previously the function didn't distinguish between an invalid conversion and one that binds to a prvalue. Since it no longer returns a bool, I removed the _p suffix. The patch also adds the relevant class and variable templates to <type_traits>. PR c++/104477 gcc/c-family/ChangeLog: * c-common.cc (c_common_reswords): Add __reference_constructs_from_temporary and __reference_converts_from_temporary. * c-common.h (enum rid): Add RID_REF_CONSTRUCTS_FROM_TEMPORARY and RID_REF_CONVERTS_FROM_TEMPORARY. gcc/cp/ChangeLog: * call.cc (ref_conv_binds_directly_p): Rename to ... (ref_conv_binds_directly): ... this. Add a new bool parameter. Change the return type to tristate. * constraint.cc (diagnose_trait_expr): Handle CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and CPTK_REF_CONVERTS_FROM_TEMPORARY. * cp-tree.h: Include "tristate.h". (enum cp_trait_kind): Add CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and CPTK_REF_CONVERTS_FROM_TEMPORARY. (ref_conv_binds_directly_p): Rename to ... (ref_conv_binds_directly): ... this. (ref_xes_from_temporary): Declare. * cxx-pretty-print.cc (pp_cxx_trait_expression): Handle CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and CPTK_REF_CONVERTS_FROM_TEMPORARY. * method.cc (ref_xes_from_temporary): New. * parser.cc (cp_parser_primary_expression): Handle RID_REF_CONSTRUCTS_FROM_TEMPORARY and RID_REF_CONVERTS_FROM_TEMPORARY. (cp_parser_trait_expr): Likewise. (warn_for_range_copy): Adjust to call ref_conv_binds_directly. * semantics.cc (trait_expr_value): Handle CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and CPTK_REF_CONVERTS_FROM_TEMPORARY. (finish_trait_expr): Likewise. libstdc++-v3/ChangeLog: * include/std/type_traits (reference_constructs_from_temporary, reference_converts_from_temporary): New class templates. (reference_constructs_from_temporary_v, reference_converts_from_temporary_v): New variable templates. (__cpp_lib_reference_from_temporary): Define for C++23. * include/std/version (__cpp_lib_reference_from_temporary): Define for C++23. * testsuite/20_util/variable_templates_for_traits.cc: Test reference_constructs_from_temporary_v and reference_converts_from_temporary_v. * testsuite/20_util/reference_from_temporary/value.cc: New test. * testsuite/20_util/reference_from_temporary/value2.cc: New test. * testsuite/20_util/reference_from_temporary/version.cc: New test. gcc/testsuite/ChangeLog: * g++.dg/ext/reference_constructs_from_temporary1.C: New test. * g++.dg/ext/reference_converts_from_temporary1.C: New test.
Diffstat (limited to 'libstdc++-v3')
-rw-r--r--libstdc++-v3/include/std/type_traits39
-rw-r--r--libstdc++-v3/include/std/version5
-rw-r--r--libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc110
-rw-r--r--libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc28
-rw-r--r--libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc27
-rw-r--r--libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc14
6 files changed, 221 insertions, 2 deletions
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index e5f58bc2e3f..b1a1deecf66 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3505,6 +3505,45 @@ template<typename _Ret, typename _Fn, typename... _Args>
template<typename _Tp>
inline constexpr bool is_scoped_enum_v = is_scoped_enum<_Tp>::value;
+#define __cpp_lib_reference_from_temporary 202202L
+
+ /// True if _Tp is a reference type, a _Up value can be bound to _Tp in
+ /// direct-initialization, and a temporary object would be bound to
+ /// the reference, false otherwise.
+ /// @since C++23
+ template<typename _Tp, typename _Up>
+ struct reference_constructs_from_temporary
+ : public bool_constant<__reference_constructs_from_temporary(_Tp, _Up)>
+ {
+ static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{})
+ && std::__is_complete_or_unbounded(__type_identity<_Up>{}),
+ "template argument must be a complete class or an unbounded array");
+ };
+
+ /// True if _Tp is a reference type, a _Up value can be bound to _Tp in
+ /// copy-initialization, and a temporary object would be bound to
+ /// the reference, false otherwise.
+ /// @since C++23
+ template<typename _Tp, typename _Up>
+ struct reference_converts_from_temporary
+ : public bool_constant<__reference_converts_from_temporary(_Tp, _Up)>
+ {
+ static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{})
+ && std::__is_complete_or_unbounded(__type_identity<_Up>{}),
+ "template argument must be a complete class or an unbounded array");
+ };
+
+ /// @ingroup variable_templates
+ /// @since C++23
+ template<typename _Tp, typename _Up>
+ inline constexpr bool reference_constructs_from_temporary_v
+ = reference_constructs_from_temporary<_Tp, _Up>::value;
+
+ /// @ingroup variable_templates
+ /// @since C++23
+ template<typename _Tp, typename _Up>
+ inline constexpr bool reference_converts_from_temporary_v
+ = reference_converts_from_temporary<_Tp, _Up>::value;
#endif // C++23
#if _GLIBCXX_HAVE_IS_CONSTANT_EVALUATED
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 22280e1a349..5edca2f3007 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -300,10 +300,11 @@
#endif
#if __cplusplus > 202002L
-// c++2b
+// c++23
#define __cpp_lib_byteswap 202110L
#define __cpp_lib_constexpr_typeinfo 202106L
#define __cpp_lib_is_scoped_enum 202011L
+#define __cpp_lib_reference_from_temporary 202202L
#if _GLIBCXX_HOSTED
#define __cpp_lib_adaptor_iterator_pair_constructor 202106L
@@ -335,7 +336,7 @@
#define __cpp_lib_to_underlying 202102L
#define __cpp_lib_unreachable 202202L
#endif
-#endif // C++2b
+#endif // C++23
#endif // C++20
#endif // C++17
#endif // C++14
diff --git a/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc
new file mode 100644
index 00000000000..2f62e54d46d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc
@@ -0,0 +1,110 @@
+// Copyright (C) 2022 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <type_traits>
+#include <testsuite_tr1.h>
+
+#ifndef __cpp_lib_reference_from_temporary
+# error "Feature test macro for reference_from_temporary is missing in <version>"
+#elif __cpp_lib_reference_from_temporary < 202202L
+# error "Feature test macro for reference_from_temporary has wrong value in <version>"
+#endif
+
+void test01()
+{
+ using std::reference_constructs_from_temporary;
+ using std::reference_converts_from_temporary;
+ using namespace __gnu_test;
+
+ struct A { A(); };
+
+ struct B {
+ operator int();
+ explicit operator int&&();
+ };
+
+ struct C {
+ operator int();
+ explicit operator int&();
+ };
+
+ static_assert(test_property<reference_constructs_from_temporary, int, int>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, void>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, const volatile void>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, void, void>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, int>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, int&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, int&&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, long>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, long&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, long&&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, int>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, int&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, int&&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, long>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, long&>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, long&&>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&&, int>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&&, int&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&&, int&&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&&, long>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&&, long&>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&&, long&&>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, const A&, A>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, const A&, A&&>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, A&&, A>(true), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&, int[]>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, int[]>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, int&&, int[]>(false), "");
+
+ static_assert(test_property<reference_converts_from_temporary, int, int>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, void>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, const volatile void>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, void, void>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, int>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, int&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, int&&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, long>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, long&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, long&&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, int>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, int&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, int&&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, long>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, long&>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, long&&>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, int>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, int&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, int&&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, long>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, long&>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, long&&>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, const A&, A>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, const A&, A&&>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, A&&, A>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, int&, int[]>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, int[]>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, int[]>(false), "");
+
+ static_assert(test_property<reference_constructs_from_temporary, int&&, B>(false), "");
+ static_assert(test_property<reference_constructs_from_temporary, const int&, C>(false), "");
+ static_assert(test_property<reference_converts_from_temporary, int&&, B>(true), "");
+ static_assert(test_property<reference_converts_from_temporary, const int&, C>(true), "");
+}
diff --git a/libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc
new file mode 100644
index 00000000000..65770754299
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2022 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <type_traits>
+#include <string>
+
+void test01()
+{
+ static_assert(std::reference_converts_from_temporary_v<const std::string&, const char*>);
+ static_assert(std::reference_constructs_from_temporary_v<const std::string&, const char*>);
+}
diff --git a/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc b/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc
new file mode 100644
index 00000000000..f56e7c0dabc
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <version>
+
+#ifndef __cpp_lib_reference_from_temporary
+# error "Feature test macro for reference_from_temporary is missing in <version>"
+#elif __cpp_lib_reference_from_temporary < 202202L
+# error "Feature test macro for reference_from_temporary has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc
index 9b3957f7d47..2b03ad7067d 100644
--- a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc
+++ b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc
@@ -346,3 +346,17 @@ static_assert(disjunction_v<false_type, false_type,
true_type>, "");
static_assert(!disjunction_v<false_type, false_type,
false_type>, "");
+#if __cpp_lib_reference_from_temporary >= 202202L
+static_assert(std::reference_converts_from_temporary_v<int&&, int>
+ && std::reference_converts_from_temporary_v<const int&, int>
+ && !std::reference_converts_from_temporary_v<int&&, int&&>
+ && !std::reference_converts_from_temporary_v<const int&, int&&>
+ && std::reference_converts_from_temporary_v<int&&, long&&>
+ && std::reference_converts_from_temporary_v<int&&, long>, "");
+static_assert(std::reference_constructs_from_temporary_v<int&&, int>
+ && std::reference_constructs_from_temporary_v<const int&, int>
+ && !std::reference_constructs_from_temporary_v<int&&, int&&>
+ && !std::reference_constructs_from_temporary_v<const int&, int&&>
+ && std::reference_constructs_from_temporary_v<int&&, long&&>
+ && std::reference_constructs_from_temporary_v<int&&, long>, "");
+#endif