In this testcase we found ourselves trying to evaluate the address of a
CONSTRUCTOR and didn't have an object for the CONSTRUCTOR to associate
with. Fixed by handling lvalue uses of TARGET_EXPR properly. And then
we needed to fix handling of REFERENCE_TYPE parameters in the new call
mechanism.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit db782df2480507c8bd406d9610ae9ea2d5133d5e
Author: Jason Merrill <ja...@redhat.com>
Date: Wed Nov 19 15:34:51 2014 -0500
PR c++/63885
* constexpr.c (cxx_eval_constant_expression) [PARM_DECL]: Don't
complain yet about a reference.
[TARGET_EXPR]: Handle TARGET_EXPR with addr == true.
[ADDR_EXPR]: Make sure we don't take the address of a CONSTRUCTOR.
(cxx_bind_parameters_in_call): In the new scheme addr is always false.
* typeck.c (build_address): Don't take the address of a CONSTRUCTOR.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 5268545..41867b8 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1061,6 +1061,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
x = ctx->object;
x = cp_build_addr_expr (x, tf_warning_or_error);
}
+ bool addr = false;
if (parms && DECL_BY_REFERENCE (parms) && !use_new_call)
{
/* cp_genericize made this a reference for argument passing, but
@@ -1071,9 +1072,9 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
gcc_assert (TREE_CODE (TREE_TYPE (x)) == REFERENCE_TYPE);
type = TREE_TYPE (type);
x = convert_from_reference (x);
+ addr = true;
}
- arg = cxx_eval_constant_expression (ctx, x,
- TREE_CODE (type) == REFERENCE_TYPE,
+ arg = cxx_eval_constant_expression (ctx, x, addr,
non_constant_p, overflow_p);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p && ctx->quiet)
@@ -2854,6 +2855,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = *p;
else if (addr)
/* Defer in case this is only used for its type. */;
+ else if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE)
+ /* Defer, there's no lvalue->rvalue conversion. */;
else if (is_empty_class (TREE_TYPE (t)))
{
/* If the class is empty, we aren't actually loading anything. */
@@ -2934,6 +2937,12 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if (!*non_constant_p)
/* Adjust the type of the result to the type of the temporary. */
r = adjust_temp_type (TREE_TYPE (t), r);
+ if (addr)
+ {
+ tree slot = TARGET_EXPR_SLOT (t);
+ ctx->values->put (slot, r);
+ return slot;
+ }
break;
case INIT_EXPR:
@@ -2995,6 +3004,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p)
return t;
+ gcc_checking_assert (TREE_CODE (op) != CONSTRUCTOR);
/* This function does more aggressive folding than fold itself. */
r = build_fold_addr_expr_with_type (op, TREE_TYPE (t));
if (TREE_CODE (r) == ADDR_EXPR && TREE_OPERAND (r, 0) == oldop)
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 53fe67a..7156851 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5305,6 +5305,7 @@ build_address (tree t)
{
if (error_operand_p (t) || !cxx_mark_addressable (t))
return error_mark_node;
+ gcc_checking_assert (TREE_CODE (t) != CONSTRUCTOR);
t = build_fold_addr_expr (t);
if (TREE_CODE (t) != ADDR_EXPR)
t = rvalue (t);
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref8.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref8.C
new file mode 100644
index 0000000..04de9c7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref8.C
@@ -0,0 +1,61 @@
+// PR c++/63885
+// { dg-do compile { target c++11 } }
+
+template<class T> struct remove_reference { typedef T type; };
+template<class T> struct remove_reference<T&> { typedef T type; };
+template<class T> struct remove_reference<T&&> { typedef T type; };
+
+template<class T> struct is_lvalue_reference { static const bool value = false; };
+template<class T> struct is_lvalue_reference<T&> { static const bool value = true; };
+
+template <bool B, class U, class V> struct conditional;
+template <class U, class V> struct conditional<true, U, V> { typedef U type; };
+template <class U, class V> struct conditional<false, U, V> { typedef V type; };
+
+template<typename _Tp> constexpr _Tp&&
+forward(typename remove_reference<_Tp>::type& __t) noexcept
+{ return static_cast<_Tp&&>(__t); }
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename C> struct member_forward
+{
+ typedef typename remove_reference <C>::type::type T;
+ typedef typename conditional
+ <
+ is_lvalue_reference <C &&>::value,
+ T&,
+ T
+ >::type type;
+};
+
+template <typename C> using member_forward_t = typename member_forward <C>::type;
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <int , typename > struct __get;
+template < typename T> struct __get <0, T>
+{
+ constexpr static auto value (T arg)
+ -> decltype ((forward <member_forward_t <T>> (arg.t)))
+ {
+ return forward <member_forward_t <T>> (arg.t);
+ }
+};
+
+template <int N, typename T> constexpr auto get (T && arg)
+ -> decltype (__get <N, T &&>::value (forward <T> (arg)))
+{
+ return __get <N, T &&>::value (forward <T> (arg));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> struct S
+{
+ typedef T type;
+ T t;
+
+ template <typename U> constexpr S (U && u) : t (forward <U> (u)) {}
+};
+static_assert (get <0> (S <int &&> (1)) == 1, ""); // g++ 4.9 passes, g++ trunk r217559 fails