Skip to content

Commit

Permalink
c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477]
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mpolacek committed Jul 15, 2022
1 parent 0a8edfb commit 9a15d3b
Show file tree
Hide file tree
Showing 17 changed files with 744 additions and 24 deletions.
4 changes: 4 additions & 0 deletions gcc/c-family/c-common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,10 @@ const struct c_common_resword c_common_reswords[] =
{ "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY },
{ "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY },
{ "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE, D_CXXONLY },
{ "__reference_constructs_from_temporary", RID_REF_CONSTRUCTS_FROM_TEMPORARY,
D_CXXONLY },
{ "__reference_converts_from_temporary", RID_REF_CONVERTS_FROM_TEMPORARY,
D_CXXONLY },

/* C++ transactional memory. */
{ "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM },
Expand Down
2 changes: 2 additions & 0 deletions gcc/c-family/c-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ enum rid
RID_IS_UNION, RID_UNDERLYING_TYPE,
RID_IS_ASSIGNABLE, RID_IS_CONSTRUCTIBLE,
RID_IS_NOTHROW_ASSIGNABLE, RID_IS_NOTHROW_CONSTRUCTIBLE,
RID_REF_CONSTRUCTS_FROM_TEMPORARY,
RID_REF_CONVERTS_FROM_TEMPORARY,

/* C++11 */
RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,
Expand Down
20 changes: 13 additions & 7 deletions gcc/cp/call.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9109,21 +9109,27 @@ conv_binds_ref_to_prvalue (conversion *c)
return conv_is_prvalue (next_conversion (c));
}

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

bool
ref_conv_binds_directly_p (tree type, tree expr)
tristate
ref_conv_binds_directly (tree type, tree expr, bool direct_init_p /*= false*/)
{
gcc_assert (TYPE_REF_P (type));

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

const int flags = direct_init_p ? LOOKUP_NORMAL : LOOKUP_IMPLICIT;
conversion *conv = implicit_conversion (type, TREE_TYPE (expr), expr,
/*c_cast_p=*/false,
LOOKUP_IMPLICIT, tf_none);
bool ret = conv && !conv->bad_p && !conv_binds_ref_to_prvalue (conv);
/*c_cast_p=*/false, flags, tf_none);
tristate ret (tristate::TS_UNKNOWN);
if (conv && !conv->bad_p)
ret = tristate (!conv_binds_ref_to_prvalue (conv));

/* Free all the conversions we allocated. */
obstack_free (&conversion_obstack, p);
Expand Down
8 changes: 8 additions & 0 deletions gcc/cp/constraint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3697,6 +3697,14 @@ diagnose_trait_expr (tree expr, tree args)
case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
inform (loc, " %qT does not have unique object representations", t1);
break;
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
inform (loc, " %qT is not a reference that binds to a temporary "
"object of type %qT (direct-initialization)", t1, t2);
break;
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
inform (loc, " %qT is not a reference that binds to a temporary "
"object of type %qT (copy-initialization)", t1, t2);
break;
case CPTK_BASES:
case CPTK_DIRECT_BASES:
case CPTK_UNDERLYING_TYPE:
Expand Down
8 changes: 6 additions & 2 deletions gcc/cp/cp-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see
#include "tm.h"
#include "hard-reg-set.h"
#include "function.h"
#include "tristate.h"

/* In order for the format checking to accept the C++ front end
diagnostic framework extensions, you must include this file before
Expand Down Expand Up @@ -1397,7 +1398,9 @@ enum cp_trait_kind
CPTK_IS_ASSIGNABLE,
CPTK_IS_CONSTRUCTIBLE,
CPTK_IS_NOTHROW_ASSIGNABLE,
CPTK_IS_NOTHROW_CONSTRUCTIBLE
CPTK_IS_NOTHROW_CONSTRUCTIBLE,
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY,
CPTK_REF_CONVERTS_FROM_TEMPORARY
};

/* The types that we are processing. */
Expand Down Expand Up @@ -6520,7 +6523,7 @@ extern bool sufficient_parms_p (const_tree);
extern tree type_decays_to (tree);
extern tree extract_call_expr (tree);
extern tree build_trivial_dtor_call (tree, bool = false);
extern bool ref_conv_binds_directly_p (tree, tree);
extern tristate ref_conv_binds_directly (tree, tree, bool = false);
extern tree build_user_type_conversion (tree, tree, int,
tsubst_flags_t);
extern tree build_new_function_call (tree, vec<tree, va_gc> **,
Expand Down Expand Up @@ -7105,6 +7108,7 @@ extern tree forward_parm (tree);
extern bool is_trivially_xible (enum tree_code, tree, tree);
extern bool is_nothrow_xible (enum tree_code, tree, tree);
extern bool is_xible (enum tree_code, tree, tree);
extern bool ref_xes_from_temporary (tree, tree, bool);
extern tree get_defaulted_eh_spec (tree, tsubst_flags_t = tf_warning_or_error);
extern bool maybe_explain_implicit_delete (tree);
extern void explain_implicit_non_constexpr (tree);
Expand Down
6 changes: 6 additions & 0 deletions gcc/cp/cxx-pretty-print.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2696,6 +2696,12 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
pp_cxx_ws_string (pp, "__is_nothrow_constructible");
break;
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
pp_cxx_ws_string (pp, "__reference_constructs_from_temporary");
break;
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
pp_cxx_ws_string (pp, "__reference_converts_from_temporary");
break;

default:
gcc_unreachable ();
Expand Down
25 changes: 25 additions & 0 deletions gcc/cp/method.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2211,6 +2211,31 @@ is_xible (enum tree_code code, tree to, tree from)
return !!expr;
}

/* Return true iff conjunction_v<is_reference<T>, is_constructible<T, U>> is
true, and the initialization
T t(VAL<U>); // DIRECT_INIT_P
or
T t = VAL<U>; // !DIRECT_INIT_P
binds t to a temporary object whose lifetime is extended.
VAL<T> is defined in [meta.unary.prop]:
-- If T is a reference or function type, VAL<T> is an expression with the
same type and value category as declval<T>().
-- Otherwise, VAL<T> is a prvalue that initially has type T. */

bool
ref_xes_from_temporary (tree to, tree from, bool direct_init_p)
{
/* Check is_reference<T>. */
if (!TYPE_REF_P (to))
return false;
/* We don't check is_constructible<T, U>: if T isn't constructible
from U, we won't be able to create a conversion. */
tree val = build_stub_object (from);
if (!TYPE_REF_P (from) && TREE_CODE (from) != FUNCTION_TYPE)
val = CLASS_TYPE_P (from) ? force_rvalue (val, tf_none) : rvalue (val);
return ref_conv_binds_directly (to, val, direct_init_p).is_false ();
}

/* Categorize various special_function_kinds. */
#define SFK_CTOR_P(sfk) \
((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
Expand Down
36 changes: 23 additions & 13 deletions gcc/cp/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5917,6 +5917,8 @@ cp_parser_primary_expression (cp_parser *parser,
case RID_IS_CONSTRUCTIBLE:
case RID_IS_NOTHROW_ASSIGNABLE:
case RID_IS_NOTHROW_CONSTRUCTIBLE:
case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
case RID_REF_CONVERTS_FROM_TEMPORARY:
return cp_parser_trait_expr (parser, token->keyword);

// C++ concepts
Expand Down Expand Up @@ -10988,6 +10990,14 @@ cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE;
variadic = true;
break;
case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
kind = CPTK_REF_CONSTRUCTS_FROM_TEMPORARY;
binary = true;
break;
case RID_REF_CONVERTS_FROM_TEMPORARY:
kind = CPTK_REF_CONVERTS_FROM_TEMPORARY;
binary = true;
break;
default:
gcc_unreachable ();
}
Expand Down Expand Up @@ -13811,7 +13821,7 @@ warn_for_range_copy (tree decl, tree expr)

if (TYPE_REF_P (type))
{
if (glvalue_p (expr) && !ref_conv_binds_directly_p (type, expr))
if (glvalue_p (expr) && ref_conv_binds_directly (type, expr).is_false ())
{
auto_diagnostic_group d;
if (warning_at (loc, OPT_Wrange_loop_construct,
Expand Down Expand Up @@ -13839,20 +13849,20 @@ warn_for_range_copy (tree decl, tree expr)
&& trivially_copyable_p (type)))
return;

/* If we can initialize a reference directly, suggest that to avoid the
copy. */
tree rtype = cp_build_reference_type (type, /*rval*/false);
/* If we could initialize the reference directly, it wouldn't involve any
copies. */
if (!ref_conv_binds_directly_p (rtype, expr))
return;

auto_diagnostic_group d;
if (warning_at (loc, OPT_Wrange_loop_construct,
"loop variable %qD creates a copy from type %qT",
decl, type))
if (ref_conv_binds_directly (rtype, expr).is_true ())
{
gcc_rich_location richloc (loc);
richloc.add_fixit_insert_before ("&");
inform (&richloc, "use reference type to prevent copying");
auto_diagnostic_group d;
if (warning_at (loc, OPT_Wrange_loop_construct,
"loop variable %qD creates a copy from type %qT",
decl, type))
{
gcc_rich_location richloc (loc);
richloc.add_fixit_insert_before ("&");
inform (&richloc, "use reference type to prevent copying");
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions gcc/cp/semantics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12007,6 +12007,12 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
return is_nothrow_xible (INIT_EXPR, type1, type2);

case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);

case CPTK_REF_CONVERTS_FROM_TEMPORARY:
return ref_xes_from_temporary (type1, type2, /*direct_init=*/false);

default:
gcc_unreachable ();
return false;
Expand Down Expand Up @@ -12088,6 +12094,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
case CPTK_IS_NOTHROW_ASSIGNABLE:
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
if (!check_trait_type (type1)
|| !check_trait_type (type2))
return error_mark_node;
Expand Down
Loading

0 comments on commit 9a15d3b

Please sign in to comment.