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

Reply via email to