Skip to content

Commit 9a15d3b

Browse files
committed
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.
1 parent 0a8edfb commit 9a15d3b

17 files changed

+744
-24
lines changed

gcc/c-family/c-common.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@ const struct c_common_resword c_common_reswords[] =
537537
{ "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY },
538538
{ "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY },
539539
{ "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE, D_CXXONLY },
540+
{ "__reference_constructs_from_temporary", RID_REF_CONSTRUCTS_FROM_TEMPORARY,
541+
D_CXXONLY },
542+
{ "__reference_converts_from_temporary", RID_REF_CONVERTS_FROM_TEMPORARY,
543+
D_CXXONLY },
540544

541545
/* C++ transactional memory. */
542546
{ "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM },

gcc/c-family/c-common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ enum rid
184184
RID_IS_UNION, RID_UNDERLYING_TYPE,
185185
RID_IS_ASSIGNABLE, RID_IS_CONSTRUCTIBLE,
186186
RID_IS_NOTHROW_ASSIGNABLE, RID_IS_NOTHROW_CONSTRUCTIBLE,
187+
RID_REF_CONSTRUCTS_FROM_TEMPORARY,
188+
RID_REF_CONVERTS_FROM_TEMPORARY,
187189

188190
/* C++11 */
189191
RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,

gcc/cp/call.cc

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9109,21 +9109,27 @@ conv_binds_ref_to_prvalue (conversion *c)
91099109
return conv_is_prvalue (next_conversion (c));
91109110
}
91119111

9112-
/* True iff converting EXPR to a reference type TYPE does not involve
9113-
creating a temporary. */
9112+
/* Return tristate::TS_TRUE if converting EXPR to a reference type TYPE does
9113+
not involve creating a temporary. Return tristate::TS_FALSE if converting
9114+
EXPR to a reference type TYPE binds the reference to a temporary. If the
9115+
conversion is invalid or bad, return tristate::TS_UNKNOWN. DIRECT_INIT_P
9116+
says whether the conversion should be done in direct- or copy-initialization
9117+
context. */
91149118

9115-
bool
9116-
ref_conv_binds_directly_p (tree type, tree expr)
9119+
tristate
9120+
ref_conv_binds_directly (tree type, tree expr, bool direct_init_p /*= false*/)
91179121
{
91189122
gcc_assert (TYPE_REF_P (type));
91199123

91209124
/* Get the high-water mark for the CONVERSION_OBSTACK. */
91219125
void *p = conversion_obstack_alloc (0);
91229126

9127+
const int flags = direct_init_p ? LOOKUP_NORMAL : LOOKUP_IMPLICIT;
91239128
conversion *conv = implicit_conversion (type, TREE_TYPE (expr), expr,
9124-
/*c_cast_p=*/false,
9125-
LOOKUP_IMPLICIT, tf_none);
9126-
bool ret = conv && !conv->bad_p && !conv_binds_ref_to_prvalue (conv);
9129+
/*c_cast_p=*/false, flags, tf_none);
9130+
tristate ret (tristate::TS_UNKNOWN);
9131+
if (conv && !conv->bad_p)
9132+
ret = tristate (!conv_binds_ref_to_prvalue (conv));
91279133

91289134
/* Free all the conversions we allocated. */
91299135
obstack_free (&conversion_obstack, p);

gcc/cp/constraint.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3697,6 +3697,14 @@ diagnose_trait_expr (tree expr, tree args)
36973697
case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
36983698
inform (loc, " %qT does not have unique object representations", t1);
36993699
break;
3700+
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
3701+
inform (loc, " %qT is not a reference that binds to a temporary "
3702+
"object of type %qT (direct-initialization)", t1, t2);
3703+
break;
3704+
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
3705+
inform (loc, " %qT is not a reference that binds to a temporary "
3706+
"object of type %qT (copy-initialization)", t1, t2);
3707+
break;
37003708
case CPTK_BASES:
37013709
case CPTK_DIRECT_BASES:
37023710
case CPTK_UNDERLYING_TYPE:

gcc/cp/cp-tree.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see
2424
#include "tm.h"
2525
#include "hard-reg-set.h"
2626
#include "function.h"
27+
#include "tristate.h"
2728

2829
/* In order for the format checking to accept the C++ front end
2930
diagnostic framework extensions, you must include this file before
@@ -1397,7 +1398,9 @@ enum cp_trait_kind
13971398
CPTK_IS_ASSIGNABLE,
13981399
CPTK_IS_CONSTRUCTIBLE,
13991400
CPTK_IS_NOTHROW_ASSIGNABLE,
1400-
CPTK_IS_NOTHROW_CONSTRUCTIBLE
1401+
CPTK_IS_NOTHROW_CONSTRUCTIBLE,
1402+
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY,
1403+
CPTK_REF_CONVERTS_FROM_TEMPORARY
14011404
};
14021405

14031406
/* The types that we are processing. */
@@ -6520,7 +6523,7 @@ extern bool sufficient_parms_p (const_tree);
65206523
extern tree type_decays_to (tree);
65216524
extern tree extract_call_expr (tree);
65226525
extern tree build_trivial_dtor_call (tree, bool = false);
6523-
extern bool ref_conv_binds_directly_p (tree, tree);
6526+
extern tristate ref_conv_binds_directly (tree, tree, bool = false);
65246527
extern tree build_user_type_conversion (tree, tree, int,
65256528
tsubst_flags_t);
65266529
extern tree build_new_function_call (tree, vec<tree, va_gc> **,
@@ -7105,6 +7108,7 @@ extern tree forward_parm (tree);
71057108
extern bool is_trivially_xible (enum tree_code, tree, tree);
71067109
extern bool is_nothrow_xible (enum tree_code, tree, tree);
71077110
extern bool is_xible (enum tree_code, tree, tree);
7111+
extern bool ref_xes_from_temporary (tree, tree, bool);
71087112
extern tree get_defaulted_eh_spec (tree, tsubst_flags_t = tf_warning_or_error);
71097113
extern bool maybe_explain_implicit_delete (tree);
71107114
extern void explain_implicit_non_constexpr (tree);

gcc/cp/cxx-pretty-print.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2696,6 +2696,12 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
26962696
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
26972697
pp_cxx_ws_string (pp, "__is_nothrow_constructible");
26982698
break;
2699+
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
2700+
pp_cxx_ws_string (pp, "__reference_constructs_from_temporary");
2701+
break;
2702+
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
2703+
pp_cxx_ws_string (pp, "__reference_converts_from_temporary");
2704+
break;
26992705

27002706
default:
27012707
gcc_unreachable ();

gcc/cp/method.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,31 @@ is_xible (enum tree_code code, tree to, tree from)
22112211
return !!expr;
22122212
}
22132213

2214+
/* Return true iff conjunction_v<is_reference<T>, is_constructible<T, U>> is
2215+
true, and the initialization
2216+
T t(VAL<U>); // DIRECT_INIT_P
2217+
or
2218+
T t = VAL<U>; // !DIRECT_INIT_P
2219+
binds t to a temporary object whose lifetime is extended.
2220+
VAL<T> is defined in [meta.unary.prop]:
2221+
-- If T is a reference or function type, VAL<T> is an expression with the
2222+
same type and value category as declval<T>().
2223+
-- Otherwise, VAL<T> is a prvalue that initially has type T. */
2224+
2225+
bool
2226+
ref_xes_from_temporary (tree to, tree from, bool direct_init_p)
2227+
{
2228+
/* Check is_reference<T>. */
2229+
if (!TYPE_REF_P (to))
2230+
return false;
2231+
/* We don't check is_constructible<T, U>: if T isn't constructible
2232+
from U, we won't be able to create a conversion. */
2233+
tree val = build_stub_object (from);
2234+
if (!TYPE_REF_P (from) && TREE_CODE (from) != FUNCTION_TYPE)
2235+
val = CLASS_TYPE_P (from) ? force_rvalue (val, tf_none) : rvalue (val);
2236+
return ref_conv_binds_directly (to, val, direct_init_p).is_false ();
2237+
}
2238+
22142239
/* Categorize various special_function_kinds. */
22152240
#define SFK_CTOR_P(sfk) \
22162241
((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)

gcc/cp/parser.cc

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5917,6 +5917,8 @@ cp_parser_primary_expression (cp_parser *parser,
59175917
case RID_IS_CONSTRUCTIBLE:
59185918
case RID_IS_NOTHROW_ASSIGNABLE:
59195919
case RID_IS_NOTHROW_CONSTRUCTIBLE:
5920+
case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
5921+
case RID_REF_CONVERTS_FROM_TEMPORARY:
59205922
return cp_parser_trait_expr (parser, token->keyword);
59215923

59225924
// C++ concepts
@@ -10988,6 +10990,14 @@ cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
1098810990
kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE;
1098910991
variadic = true;
1099010992
break;
10993+
case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
10994+
kind = CPTK_REF_CONSTRUCTS_FROM_TEMPORARY;
10995+
binary = true;
10996+
break;
10997+
case RID_REF_CONVERTS_FROM_TEMPORARY:
10998+
kind = CPTK_REF_CONVERTS_FROM_TEMPORARY;
10999+
binary = true;
11000+
break;
1099111001
default:
1099211002
gcc_unreachable ();
1099311003
}
@@ -13811,7 +13821,7 @@ warn_for_range_copy (tree decl, tree expr)
1381113821

1381213822
if (TYPE_REF_P (type))
1381313823
{
13814-
if (glvalue_p (expr) && !ref_conv_binds_directly_p (type, expr))
13824+
if (glvalue_p (expr) && ref_conv_binds_directly (type, expr).is_false ())
1381513825
{
1381613826
auto_diagnostic_group d;
1381713827
if (warning_at (loc, OPT_Wrange_loop_construct,
@@ -13839,20 +13849,20 @@ warn_for_range_copy (tree decl, tree expr)
1383913849
&& trivially_copyable_p (type)))
1384013850
return;
1384113851

13852+
/* If we can initialize a reference directly, suggest that to avoid the
13853+
copy. */
1384213854
tree rtype = cp_build_reference_type (type, /*rval*/false);
13843-
/* If we could initialize the reference directly, it wouldn't involve any
13844-
copies. */
13845-
if (!ref_conv_binds_directly_p (rtype, expr))
13846-
return;
13847-
13848-
auto_diagnostic_group d;
13849-
if (warning_at (loc, OPT_Wrange_loop_construct,
13850-
"loop variable %qD creates a copy from type %qT",
13851-
decl, type))
13855+
if (ref_conv_binds_directly (rtype, expr).is_true ())
1385213856
{
13853-
gcc_rich_location richloc (loc);
13854-
richloc.add_fixit_insert_before ("&");
13855-
inform (&richloc, "use reference type to prevent copying");
13857+
auto_diagnostic_group d;
13858+
if (warning_at (loc, OPT_Wrange_loop_construct,
13859+
"loop variable %qD creates a copy from type %qT",
13860+
decl, type))
13861+
{
13862+
gcc_rich_location richloc (loc);
13863+
richloc.add_fixit_insert_before ("&");
13864+
inform (&richloc, "use reference type to prevent copying");
13865+
}
1385613866
}
1385713867
}
1385813868

gcc/cp/semantics.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12007,6 +12007,12 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
1200712007
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
1200812008
return is_nothrow_xible (INIT_EXPR, type1, type2);
1200912009

12010+
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
12011+
return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
12012+
12013+
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
12014+
return ref_xes_from_temporary (type1, type2, /*direct_init=*/false);
12015+
1201012016
default:
1201112017
gcc_unreachable ();
1201212018
return false;
@@ -12088,6 +12094,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
1208812094
case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
1208912095
case CPTK_IS_NOTHROW_ASSIGNABLE:
1209012096
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
12097+
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
12098+
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
1209112099
if (!check_trait_type (type1)
1209212100
|| !check_trait_type (type2))
1209312101
return error_mark_node;

0 commit comments

Comments
 (0)