Patchwork C++ PATCH to require real lvalues in expressions

login
register
mail settings
Submitter Jason Merrill
Date Sept. 27, 2010, 8:23 p.m.
Message ID <4CA0FD49.2090608@redhat.com>
Download mbox | patch
Permalink /patch/65918/
State New
Headers show

Comments

Jason Merrill - Sept. 27, 2010, 8:23 p.m.
The C++ standard has always required a real lvalue for the first operand 
of &, =, += and such, but G++ has allowed class temporaries and, in 
C++0x mode, rvalue references.  This patch corrects that.  With the rise 
of SFINAE-based template dispatch, this can lead us to select the wrong 
template; and even in the absence of SFINAE I don't see much point in 
this permissiveness, especially since we weren't even giving a pedwarn 
for it.

Tested x86_64-cp-linux-gnu, applied to trunk.
Mark Mitchell - Sept. 28, 2010, 2:23 p.m.
On 9/27/2010 1:23 PM, Jason Merrill wrote:

> The C++ standard has always required a real lvalue for the first operand
> of &, =, += and such, but G++ has allowed class temporaries and, in
> C++0x mode, rvalue references.  This patch corrects that.  With the rise
> of SFINAE-based template dispatch, this can lead us to select the wrong
> template; and even in the absence of SFINAE I don't see much point in
> this permissiveness, especially since we weren't even giving a pedwarn
> for it.

I have no love of this extension, but doesn't the fact that we weren't
warning increase the chance that we'll break a lot of code with this
change?  Especially given that we even had tests that depended on it?
Without question, in our stricter mode we should reject this code, but
should we try to make it keep working with -fpermissive?

Patch

commit 0d4843a8a30cdbf32fb1ff74e79bf1c9ae38d447
Author: Jason Merrill <jason@redhat.com>
Date:   Sun Sep 26 11:57:58 2010 -0400

    	Require lvalues as specified by the standard.
    	* typeck.c (lvalue_or_else): Use real_lvalue_p.
    	(cp_build_addr_expr_1): Split out of cp_build_unary_op.
    	(cp_build_addr_expr, cp_build_addr_expr_strict): Interfaces.
    	(decay_conversion, get_member_function_from_ptrfunc): Adjust.
    	(build_x_unary_op, build_reinterpret_cast_1): Adjust.
    	(build_const_cast_1): Adjust.
    	* cp-tree.h: Declare new fns.
    	* call.c (build_this, convert_like_real, build_over_call): Adjust.
    	(initialize_reference): Adjust.
    	* class.c (build_base_path, convert_to_base_statically): Adjust.
    	(build_vfn_ref, resolve_address_of_overloaded_function): Adjust.
    	* cvt.c (build_up_reference, convert_to_reference): Adjust.
    	* decl.c (register_dtor_fn): Adjust.
    	* decl2.c (build_offset_ref_call_from_tree): Adjust.
    	* except.c (initialize_handler_parm): Adjust.
    	* init.c (build_offset_ref, build_delete, build_vec_delete): Adjust.
    	* rtti.c (build_dynamic_cast_1, tinfo_base_init): Adjust.
    	* tree.c (stabilize_expr): Adjust.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c3a5799..2e7083d 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -2740,7 +2740,7 @@  build_this (tree obj)
   if (processing_template_decl)
     return build_address (obj);
 
-  return cp_build_unary_op (ADDR_EXPR, obj, 0, tf_warning_or_error);
+  return cp_build_addr_expr (obj, tf_warning_or_error);
 }
 
 /* Returns true iff functions are equivalent. Equivalent functions are
@@ -5157,7 +5157,7 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  /* We are going to bind a reference directly to a base-class
 	     subobject of EXPR.  */
 	  /* Build an expression for `*((base*) &expr)'.  */
-	  expr = cp_build_unary_op (ADDR_EXPR, expr, 0, complain);
+	  expr = cp_build_addr_expr (expr, complain);
 	  expr = convert_to_base (expr, build_pointer_type (totype),
 				  !c_cast_p, /*nonnull=*/true, complain);
 	  expr = cp_build_indirect_ref (expr, RO_IMPLICIT_CONVERSION, complain);
@@ -5206,8 +5206,7 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
            VA_ARG_EXPR and CONSTRUCTOR expressions are special cases
            that need temporaries, even when their types are reference
            compatible with the type of reference being bound, so the
-           upcoming call to cp_build_unary_op (ADDR_EXPR, expr, ...)
-           doesn't fail.  */
+           upcoming call to cp_build_addr_expr doesn't fail.  */
 	if (convs->need_temporary_p
 	    || TREE_CODE (expr) == CONSTRUCTOR
 	    || TREE_CODE (expr) == VA_ARG_EXPR)
@@ -5264,7 +5263,7 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 
 	/* Take the address of the thing to which we will bind the
 	   reference.  */
-	expr = cp_build_unary_op (ADDR_EXPR, expr, 1, complain);
+	expr = cp_build_addr_expr (expr, complain);
 	if (expr == error_mark_node)
 	  return error_mark_node;
 
@@ -6011,7 +6010,7 @@  build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 
 	  arg2 = TYPE_SIZE_UNIT (as_base);
 	  arg1 = arg;
-	  arg0 = cp_build_unary_op (ADDR_EXPR, to, 0, complain);
+	  arg0 = cp_build_addr_expr (to, complain);
 
 	  if (!can_trust_pointer_alignment ())
 	    {
@@ -7994,7 +7993,7 @@  initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
 	    }
 	  else
 	    /* Take the address of EXPR.  */
-	    expr = cp_build_unary_op (ADDR_EXPR, expr, 0, tf_warning_or_error);
+	    expr = cp_build_addr_expr (expr, tf_warning_or_error);
 	  /* If a BASE_CONV was required, perform it now.  */
 	  if (base_conv_type)
 	    expr = (perform_implicit_conversion
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index c594d6a..b093ce0 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -282,7 +282,7 @@  build_base_path (enum tree_code code,
 
   if (!want_pointer)
     /* This must happen before the call to save_expr.  */
-    expr = cp_build_unary_op (ADDR_EXPR, expr, 0, tf_warning_or_error);
+    expr = cp_build_addr_expr (expr, tf_warning_or_error);
   else
     expr = mark_rvalue_use (expr);
 
@@ -557,8 +557,7 @@  convert_to_base_statically (tree expr, tree base)
 	 when processing a template because they do not handle C++-specific
 	 trees.  */
       gcc_assert (!processing_template_decl);
-      expr = cp_build_unary_op (ADDR_EXPR, expr, /*noconvert=*/1, 
-                             tf_warning_or_error);
+      expr = cp_build_addr_expr (expr, tf_warning_or_error);
       if (!integer_zerop (BINFO_OFFSET (base)))
         expr = fold_build2_loc (input_location,
 			    POINTER_PLUS_EXPR, pointer_type, expr,
@@ -661,8 +660,7 @@  build_vfn_ref (tree instance_ptr, tree idx)
      vtable entry is treated as a function pointer.  */
   if (TARGET_VTABLE_USES_DESCRIPTORS)
     aref = build1 (NOP_EXPR, TREE_TYPE (aref),
-		   cp_build_unary_op (ADDR_EXPR, aref, /*noconvert=*/1,
-                                   tf_warning_or_error));
+		   cp_build_addr_expr (aref, tf_warning_or_error));
 
   /* Remember this as a method reference, for later devirtualization.  */
   aref = build3 (OBJ_TYPE_REF, TREE_TYPE (aref), aref, instance_ptr, idx);
@@ -6464,7 +6462,7 @@  resolve_address_of_overloaded_function (tree target_type,
     }
 
   if (TYPE_PTRFN_P (target_type) || TYPE_PTRMEMFUNC_P (target_type))
-    return cp_build_unary_op (ADDR_EXPR, fn, 0, flags);
+    return cp_build_addr_expr (fn, flags);
   else
     {
       /* The target must be a REFERENCE_TYPE.  Above, cp_build_unary_op
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c78beb7..2ff0973 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5472,6 +5472,8 @@  extern tree build_x_binary_op			(enum tree_code, tree,
 extern tree build_x_array_ref			(tree, tree, tsubst_flags_t);
 extern tree build_x_unary_op			(enum tree_code, tree,
                                                  tsubst_flags_t);
+extern tree cp_build_addr_expr			(tree, tsubst_flags_t);
+extern tree cp_build_addr_expr_strict		(tree, tsubst_flags_t);
 extern tree cp_build_unary_op                   (enum tree_code, tree, int, 
                                                  tsubst_flags_t);
 extern tree unary_complex_lvalue		(enum tree_code, tree);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index ab2b6bf..00aa44a 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -327,7 +327,7 @@  build_up_reference (tree type, tree arg, int flags, tree decl)
   /* If we had a way to wrap this up, and say, if we ever needed its
      address, transform all occurrences of the register, into a memory
      reference we could win better.  */
-  rval = cp_build_unary_op (ADDR_EXPR, arg, 1, tf_warning_or_error);
+  rval = cp_build_addr_expr (arg, tf_warning_or_error);
   if (rval == error_mark_node)
     return error_mark_node;
 
@@ -471,7 +471,7 @@  convert_to_reference (tree reftype, tree expr, int convtype,
 	warning (0, "casting %qT to %qT does not dereference pointer",
 		 intype, reftype);
 
-      rval = cp_build_unary_op (ADDR_EXPR, expr, 0, tf_warning_or_error);
+      rval = cp_build_addr_expr (expr, tf_warning_or_error);
       if (rval != error_mark_node)
 	rval = convert_force (build_pointer_type (TREE_TYPE (reftype)),
 			      rval, 0);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 07eddb5..18367b5 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6354,8 +6354,8 @@  register_dtor_fn (tree decl)
 	   in, and, in general, it's cheaper to pass NULL than any
 	   other value.  */
 	addr = null_pointer_node;
-      arg2 = cp_build_unary_op (ADDR_EXPR, get_dso_handle_node (), 0,
-                                tf_warning_or_error);
+      arg2 = cp_build_addr_expr (get_dso_handle_node (),
+				 tf_warning_or_error);
       if (targetm.cxx.use_aeabi_atexit ())
 	{
 	  arg1 = cleanup;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 63197705..fcc83fb 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4009,7 +4009,7 @@  build_offset_ref_call_from_tree (tree fn, VEC(tree,gc) **args)
       make_args_non_dependent (*args);
       object = build_non_dependent_expr (object);
       if (TREE_CODE (fn) == DOTSTAR_EXPR)
-	object = cp_build_unary_op (ADDR_EXPR, object, 0, tf_warning_or_error);
+	object = cp_build_addr_expr (object, tf_warning_or_error);
       VEC_safe_insert (tree, gc, *args, 0, object);
       /* Now that the arguments are done, transform FN.  */
       fn = build_non_dependent_expr (fn);
@@ -4023,8 +4023,7 @@  build_offset_ref_call_from_tree (tree fn, VEC(tree,gc) **args)
 	void B::g() { (this->*p)(); }  */
   if (TREE_CODE (fn) == OFFSET_REF)
     {
-      tree object_addr = cp_build_unary_op (ADDR_EXPR, object, 0,
-                                         tf_warning_or_error);
+      tree object_addr = cp_build_addr_expr (object, tf_warning_or_error);
       fn = TREE_OPERAND (fn, 1);
       fn = get_member_function_from_ptrfunc (&object_addr, fn);
       VEC_safe_insert (tree, gc, *args, 0, object_addr);
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 351e685..9d19aa9 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -376,7 +376,7 @@  initialize_handler_parm (tree decl, tree exp)
      pointer catch parm with the address of the temporary.  */
   if (TREE_CODE (init_type) == REFERENCE_TYPE
       && TYPE_PTR_P (TREE_TYPE (init_type)))
-    exp = cp_build_unary_op (ADDR_EXPR, exp, 1, tf_warning_or_error);
+    exp = cp_build_addr_expr (exp, tf_warning_or_error);
 
   exp = ocp_convert (init_type, exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
 
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 189bcbe..5091d4e 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1649,8 +1649,7 @@  build_offset_ref (tree type, tree member, bool address_p)
 	  if (flag_ms_extensions)
 	    {
 	      PTRMEM_OK_P (member) = 1;
-	      return cp_build_unary_op (ADDR_EXPR, member, 0, 
-                                        tf_warning_or_error);
+	      return cp_build_addr_expr (member, tf_warning_or_error);
 	    }
 	  error ("invalid use of non-static member function %qD",
 		 TREE_OPERAND (member, 1));
@@ -3246,7 +3245,7 @@  build_delete (tree type, tree addr, special_function_kind auto_delete,
       /* Don't check PROTECT here; leave that decision to the
 	 destructor.  If the destructor is accessible, call it,
 	 else report error.  */
-      addr = cp_build_unary_op (ADDR_EXPR, addr, 0, tf_warning_or_error);
+      addr = cp_build_addr_expr (addr, tf_warning_or_error);
       if (TREE_SIDE_EFFECTS (addr))
 	addr = save_expr (addr);
 
@@ -3486,7 +3485,7 @@  build_vec_delete (tree base, tree maxindex,
 	 bad name.  */
       maxindex = array_type_nelts_total (type);
       type = strip_array_types (type);
-      base = cp_build_unary_op (ADDR_EXPR, base, 1, tf_warning_or_error);
+      base = cp_build_addr_expr (base, tf_warning_or_error);
       if (TREE_SIDE_EFFECTS (base))
 	{
 	  base_init = get_target_expr (base);
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index c994683..d7a151d 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -693,10 +693,10 @@  build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain)
 	  static_type = TYPE_MAIN_VARIANT (TREE_TYPE (exprtype));
 	  td2 = get_tinfo_decl (target_type);
 	  mark_used (td2);
-	  td2 = cp_build_unary_op (ADDR_EXPR, td2, 0, complain);
+	  td2 = cp_build_addr_expr (td2, complain);
 	  td3 = get_tinfo_decl (static_type);
 	  mark_used (td3);
-	  td3 = cp_build_unary_op (ADDR_EXPR, td3, 0, complain);
+	  td3 = cp_build_addr_expr (td3, complain);
 
 	  /* Determine how T and V are related.  */
 	  boff = dcast_base_hint (static_type, target_type);
@@ -706,7 +706,7 @@  build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain)
 
 	  expr1 = expr;
 	  if (tc == REFERENCE_TYPE)
-	    expr1 = cp_build_unary_op (ADDR_EXPR, expr1, 0, complain);
+	    expr1 = cp_build_addr_expr (expr1, complain);
 
 	  elems[0] = expr1;
 	  elems[1] = td3;
@@ -913,8 +913,7 @@  tinfo_base_init (tinfo_s *ti, tree target)
 	}
 
       vtable_ptr = get_vtable_decl (real_type, /*complete=*/1);
-      vtable_ptr = cp_build_unary_op (ADDR_EXPR, vtable_ptr, 0, 
-                                   tf_warning_or_error);
+      vtable_ptr = cp_build_addr_expr (vtable_ptr, tf_warning_or_error);
 
       /* We need to point into the middle of the vtable.  */
       vtable_ptr = build2
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index ea01d1f..d52387b 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -3008,7 +3008,7 @@  stabilize_expr (tree exp, tree* initp)
     }
   else
     {
-      exp = cp_build_unary_op (ADDR_EXPR, exp, 1, tf_warning_or_error);
+      exp = cp_build_addr_expr (exp, tf_warning_or_error);
       init_expr = get_target_expr (exp);
       exp = TARGET_EXPR_SLOT (init_expr);
       exp = cp_build_indirect_ref (exp, RO_NULL, tf_warning_or_error);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 9959e40..c25a177 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1942,7 +1942,7 @@  decay_conversion (tree exp)
   if (invalid_nonstatic_memfn_p (exp, tf_warning_or_error))
     return error_mark_node;
   if (code == FUNCTION_TYPE || is_overloaded_fn (exp))
-    return cp_build_unary_op (ADDR_EXPR, exp, 0, tf_warning_or_error);
+    return cp_build_addr_expr (exp, tf_warning_or_error);
   if (code == ARRAY_TYPE)
     {
       tree adr;
@@ -1977,7 +1977,7 @@  decay_conversion (tree exp)
 	}
       /* This way is better for a COMPONENT_REF since it can
 	 simplify the offset for a component.  */
-      adr = cp_build_unary_op (ADDR_EXPR, exp, 1, tf_warning_or_error);
+      adr = cp_build_addr_expr (exp, tf_warning_or_error);
       return cp_convert (ptrtype, adr);
     }
 
@@ -3211,8 +3211,7 @@  get_member_function_from_ptrfunc (tree *instance_ptrptr, tree function)
 	 vtable entry is treated as a function pointer.  */
       if (TARGET_VTABLE_USES_DESCRIPTORS)
 	e2 = build1 (NOP_EXPR, TREE_TYPE (e2),
-		     cp_build_unary_op (ADDR_EXPR, e2, /*noconvert=*/1,
-                                     tf_warning_or_error));
+		     cp_build_addr_expr (e2, tf_warning_or_error));
 
       e2 = fold_convert (TREE_TYPE (e3), e2);
       e1 = build_conditional_expr (e1, e2, e3, tf_warning_or_error);
@@ -4721,9 +4720,8 @@  build_x_unary_op (enum tree_code code, tree xarg, tsubst_flags_t complain)
 	      PTRMEM_OK_P (xarg) = ptrmem;
 	    }
 	}
-      else if (TREE_CODE (xarg) == TARGET_EXPR && (complain & tf_warning))
-	warning (0, "taking address of temporary");
-      exp = cp_build_unary_op (ADDR_EXPR, xarg, 0, complain);
+
+      exp = cp_build_addr_expr_strict (xarg, complain);
     }
 
   if (processing_template_decl && exp != error_mark_node)
@@ -4800,6 +4798,276 @@  build_nop (tree type, tree expr)
   return build1 (NOP_EXPR, type, expr);
 }
 
+/* Take the address of ARG, whatever that means under C++ semantics.
+   If STRICT_LVALUE is true, require an lvalue; otherwise, allow xvalues
+   and class rvalues as well.
+
+   Nothing should call this function directly; instead, callers should use
+   cp_build_addr_expr or cp_build_addr_expr_strict.  */
+
+static tree
+cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
+{
+  tree argtype;
+  tree val;
+
+  if (!arg || error_operand_p (arg))
+    return error_mark_node;
+
+  arg = mark_lvalue_use (arg);
+  argtype = lvalue_type (arg);
+
+  gcc_assert (TREE_CODE (arg) != IDENTIFIER_NODE
+	      || !IDENTIFIER_OPNAME_P (arg));
+
+  if (TREE_CODE (arg) == COMPONENT_REF && type_unknown_p (arg)
+      && !really_overloaded_fn (TREE_OPERAND (arg, 1)))
+    {
+      /* They're trying to take the address of a unique non-static
+	 member function.  This is ill-formed (except in MS-land),
+	 but let's try to DTRT.
+	 Note: We only handle unique functions here because we don't
+	 want to complain if there's a static overload; non-unique
+	 cases will be handled by instantiate_type.  But we need to
+	 handle this case here to allow casts on the resulting PMF.
+	 We could defer this in non-MS mode, but it's easier to give
+	 a useful error here.  */
+
+      /* Inside constant member functions, the `this' pointer
+	 contains an extra const qualifier.  TYPE_MAIN_VARIANT
+	 is used here to remove this const from the diagnostics
+	 and the created OFFSET_REF.  */
+      tree base = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (arg, 0)));
+      tree fn = get_first_fn (TREE_OPERAND (arg, 1));
+      mark_used (fn);
+
+      if (! flag_ms_extensions)
+	{
+	  tree name = DECL_NAME (fn);
+	  if (!(complain & tf_error))
+	    return error_mark_node;
+	  else if (current_class_type
+		   && TREE_OPERAND (arg, 0) == current_class_ref)
+	    /* An expression like &memfn.  */
+	    permerror (input_location, "ISO C++ forbids taking the address of an unqualified"
+		       " or parenthesized non-static member function to form"
+		       " a pointer to member function.  Say %<&%T::%D%>",
+		       base, name);
+	  else
+	    permerror (input_location, "ISO C++ forbids taking the address of a bound member"
+		       " function to form a pointer to member function."
+		       "  Say %<&%T::%D%>",
+		       base, name);
+	}
+      arg = build_offset_ref (base, fn, /*address_p=*/true);
+    }
+
+  /* Uninstantiated types are all functions.  Taking the
+     address of a function is a no-op, so just return the
+     argument.  */
+  if (type_unknown_p (arg))
+    return build1 (ADDR_EXPR, unknown_type_node, arg);
+
+  if (TREE_CODE (arg) == OFFSET_REF)
+    /* We want a pointer to member; bypass all the code for actually taking
+       the address of something.  */
+    goto offset_ref;
+
+  /* Anything not already handled and not a true memory reference
+     is an error.  */
+  if (TREE_CODE (argtype) != FUNCTION_TYPE
+      && TREE_CODE (argtype) != METHOD_TYPE)
+    {
+      bool win = strict_lvalue ? real_lvalue_p (arg) : lvalue_p (arg);
+      if (!win)
+	{
+	  if (complain & tf_error)
+	    lvalue_error (lv_addressof);
+	  return error_mark_node;
+	}
+    }
+
+  if (TREE_CODE (argtype) == REFERENCE_TYPE)
+    {
+      tree type = build_pointer_type (TREE_TYPE (argtype));
+      arg = build1 (CONVERT_EXPR, type, arg);
+      return arg;
+    }
+  else if (pedantic && DECL_MAIN_P (arg))
+    {
+      /* ARM $3.4 */
+      /* Apparently a lot of autoconf scripts for C++ packages do this,
+	 so only complain if -pedantic.  */
+      if (complain & (flag_pedantic_errors ? tf_error : tf_warning))
+	pedwarn (input_location, OPT_pedantic,
+		 "ISO C++ forbids taking address of function %<::main%>");
+      else if (flag_pedantic_errors)
+	return error_mark_node;
+    }
+
+  /* Let &* cancel out to simplify resulting code.  */
+  if (TREE_CODE (arg) == INDIRECT_REF)
+    {
+      /* We don't need to have `current_class_ptr' wrapped in a
+	 NON_LVALUE_EXPR node.  */
+      if (arg == current_class_ref)
+	return current_class_ptr;
+
+      arg = TREE_OPERAND (arg, 0);
+      if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE)
+	{
+	  tree type = build_pointer_type (TREE_TYPE (TREE_TYPE (arg)));
+	  arg = build1 (CONVERT_EXPR, type, arg);
+	}
+      else
+	/* Don't let this be an lvalue.  */
+	arg = rvalue (arg);
+      return arg;
+    }
+
+  /* ??? Cope with user tricks that amount to offsetof.  */
+  if (TREE_CODE (argtype) != FUNCTION_TYPE
+      && TREE_CODE (argtype) != METHOD_TYPE
+      && argtype != unknown_type_node
+      && (val = get_base_address (arg))
+      && TREE_CODE (val) == INDIRECT_REF
+      && TREE_CONSTANT (TREE_OPERAND (val, 0)))
+    {
+      tree type = build_pointer_type (argtype);
+      tree op0 = fold_convert (type, TREE_OPERAND (val, 0));
+      tree op1 = fold_convert (sizetype, fold_offsetof (arg, val));
+      return fold_build2 (POINTER_PLUS_EXPR, type, op0, op1);
+    }
+
+  /* Handle complex lvalues (when permitted)
+     by reduction to simpler cases.  */
+  val = unary_complex_lvalue (ADDR_EXPR, arg);
+  if (val != 0)
+    return val;
+
+  switch (TREE_CODE (arg))
+    {
+    CASE_CONVERT:
+    case FLOAT_EXPR:
+    case FIX_TRUNC_EXPR:
+      /* Even if we're not being pedantic, we cannot allow this
+	 extension when we're instantiating in a SFINAE
+	 context.  */
+      if (! lvalue_p (arg) && complain == tf_none)
+	{
+	  if (complain & tf_error)
+	    permerror (input_location, "ISO C++ forbids taking the address of a cast to a non-lvalue expression");
+	  else
+	    return error_mark_node;
+	}
+      break;
+
+    case BASELINK:
+      arg = BASELINK_FUNCTIONS (arg);
+      /* Fall through.  */
+
+    case OVERLOAD:
+      arg = OVL_CURRENT (arg);
+      break;
+
+    case OFFSET_REF:
+    offset_ref:
+      /* Turn a reference to a non-static data member into a
+	 pointer-to-member.  */
+      {
+	tree type;
+	tree t;
+
+	gcc_assert (PTRMEM_OK_P (arg));
+
+	t = TREE_OPERAND (arg, 1);
+	if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE)
+	  {
+	    if (complain & tf_error)
+	      error ("cannot create pointer to reference member %qD", t);
+	    return error_mark_node;
+	  }
+
+	type = build_ptrmem_type (context_for_name_lookup (t),
+				  TREE_TYPE (t));
+	t = make_ptrmem_cst (type, TREE_OPERAND (arg, 1));
+	return t;
+      }
+
+    default:
+      break;
+    }
+
+  if (argtype != error_mark_node)
+    argtype = build_pointer_type (argtype);
+
+  /* In a template, we are processing a non-dependent expression
+     so we can just form an ADDR_EXPR with the correct type.  */
+  if (processing_template_decl || TREE_CODE (arg) != COMPONENT_REF)
+    {
+      val = build_address (arg);
+      if (TREE_CODE (arg) == OFFSET_REF)
+	PTRMEM_OK_P (val) = PTRMEM_OK_P (arg);
+    }
+  else if (TREE_CODE (TREE_OPERAND (arg, 1)) == BASELINK)
+    {
+      tree fn = BASELINK_FUNCTIONS (TREE_OPERAND (arg, 1));
+
+      /* We can only get here with a single static member
+	 function.  */
+      gcc_assert (TREE_CODE (fn) == FUNCTION_DECL
+		  && DECL_STATIC_FUNCTION_P (fn));
+      mark_used (fn);
+      val = build_address (fn);
+      if (TREE_SIDE_EFFECTS (TREE_OPERAND (arg, 0)))
+	/* Do not lose object's side effects.  */
+	val = build2 (COMPOUND_EXPR, TREE_TYPE (val),
+		      TREE_OPERAND (arg, 0), val);
+    }
+  else if (DECL_C_BIT_FIELD (TREE_OPERAND (arg, 1)))
+    {
+      if (complain & tf_error)
+	error ("attempt to take address of bit-field structure member %qD",
+	       TREE_OPERAND (arg, 1));
+      return error_mark_node;
+    }
+  else
+    {
+      tree object = TREE_OPERAND (arg, 0);
+      tree field = TREE_OPERAND (arg, 1);
+      gcc_assert (same_type_ignoring_top_level_qualifiers_p
+		  (TREE_TYPE (object), decl_type_context (field)));
+      val = build_address (arg);
+    }
+
+  if (TREE_CODE (argtype) == POINTER_TYPE
+      && TREE_CODE (TREE_TYPE (argtype)) == METHOD_TYPE)
+    {
+      build_ptrmemfunc_type (argtype);
+      val = build_ptrmemfunc (argtype, val, 0,
+			      /*c_cast_p=*/false,
+			      tf_warning_or_error);
+    }
+
+  return val;
+}
+
+/* Take the address of ARG if it has one, even if it's an rvalue.  */
+
+tree
+cp_build_addr_expr (tree arg, tsubst_flags_t complain)
+{
+  return cp_build_addr_expr_1 (arg, 0, complain);
+}
+
+/* Take the address of ARG, but only if it's an lvalue.  */
+
+tree
+cp_build_addr_expr_strict (tree arg, tsubst_flags_t complain)
+{
+  return cp_build_addr_expr_1 (arg, 1, complain);
+}
+
 /* C++: Must handle pointers to members.
 
    Perhaps type instantiation should be extended to handle conversion
@@ -5066,238 +5334,7 @@  cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
     case ADDR_EXPR:
       /* Note that this operation never does default_conversion
 	 regardless of NOCONVERT.  */
-
-      argtype = lvalue_type (arg);
-
-      arg = mark_lvalue_use (arg);
-
-      if (TREE_CODE (arg) == OFFSET_REF)
-	goto offset_ref;
-
-      if (TREE_CODE (argtype) == REFERENCE_TYPE)
-	{
-	  tree type = build_pointer_type (TREE_TYPE (argtype));
-	  arg = build1 (CONVERT_EXPR, type, arg);
-	  return arg;
-	}
-      else if (pedantic && DECL_MAIN_P (arg))
-        {
-          /* ARM $3.4 */
-	  /* Apparently a lot of autoconf scripts for C++ packages do this,
-	     so only complain if -pedantic.  */
-          if (complain & (flag_pedantic_errors ? tf_error : tf_warning))
-            pedwarn (input_location, OPT_pedantic,
-		     "ISO C++ forbids taking address of function %<::main%>");
-          else if (flag_pedantic_errors)
-            return error_mark_node;
-        }
-
-      /* Let &* cancel out to simplify resulting code.  */
-      if (TREE_CODE (arg) == INDIRECT_REF)
-	{
-	  /* We don't need to have `current_class_ptr' wrapped in a
-	     NON_LVALUE_EXPR node.  */
-	  if (arg == current_class_ref)
-	    return current_class_ptr;
-
-	  arg = TREE_OPERAND (arg, 0);
-	  if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE)
-	    {
-	      tree type = build_pointer_type (TREE_TYPE (TREE_TYPE (arg)));
-	      arg = build1 (CONVERT_EXPR, type, arg);
-	    }
-	  else
-	    /* Don't let this be an lvalue.  */
-	    arg = rvalue (arg);
-	  return arg;
-	}
-
-      /* ??? Cope with user tricks that amount to offsetof.  */
-      if (TREE_CODE (argtype) != FUNCTION_TYPE
-	  && TREE_CODE (argtype) != METHOD_TYPE
-	  && argtype != unknown_type_node
-	  && (val = get_base_address (arg))
-	  && TREE_CODE (val) == INDIRECT_REF
-	  && TREE_CONSTANT (TREE_OPERAND (val, 0)))
-	{
-	  tree type = build_pointer_type (argtype);
-	  tree op0 = fold_convert (type, TREE_OPERAND (val, 0));
-	  tree op1 = fold_convert (sizetype, fold_offsetof (arg, val));
-	  return fold_build2 (POINTER_PLUS_EXPR, type, op0, op1);
-	}
-
-      /* Uninstantiated types are all functions.  Taking the
-	 address of a function is a no-op, so just return the
-	 argument.  */
-
-      gcc_assert (TREE_CODE (arg) != IDENTIFIER_NODE
-		  || !IDENTIFIER_OPNAME_P (arg));
-
-      if (TREE_CODE (arg) == COMPONENT_REF && type_unknown_p (arg)
-	  && !really_overloaded_fn (TREE_OPERAND (arg, 1)))
-	{
-	  /* They're trying to take the address of a unique non-static
-	     member function.  This is ill-formed (except in MS-land),
-	     but let's try to DTRT.
-	     Note: We only handle unique functions here because we don't
-	     want to complain if there's a static overload; non-unique
-	     cases will be handled by instantiate_type.  But we need to
-	     handle this case here to allow casts on the resulting PMF.
-	     We could defer this in non-MS mode, but it's easier to give
-	     a useful error here.  */
-
-	  /* Inside constant member functions, the `this' pointer
-	     contains an extra const qualifier.  TYPE_MAIN_VARIANT
-	     is used here to remove this const from the diagnostics
-	     and the created OFFSET_REF.  */
-	  tree base = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (arg, 0)));
-	  tree fn = get_first_fn (TREE_OPERAND (arg, 1));
-	  mark_used (fn);
-
-	  if (! flag_ms_extensions)
-	    {
-	      tree name = DECL_NAME (fn);
-              if (!(complain & tf_error))
-                return error_mark_node;
-	      else if (current_class_type
-                       && TREE_OPERAND (arg, 0) == current_class_ref)
-                  /* An expression like &memfn.  */
-                permerror (input_location, "ISO C++ forbids taking the address of an unqualified"
-                           " or parenthesized non-static member function to form"
-                           " a pointer to member function.  Say %<&%T::%D%>",
-                           base, name);
-	      else
-		permerror (input_location, "ISO C++ forbids taking the address of a bound member"
-			   " function to form a pointer to member function."
-			   "  Say %<&%T::%D%>",
-			   base, name);
-	    }
-	  arg = build_offset_ref (base, fn, /*address_p=*/true);
-	}
-
-    offset_ref:
-      if (type_unknown_p (arg))
-	return build1 (ADDR_EXPR, unknown_type_node, arg);
-
-      /* Handle complex lvalues (when permitted)
-	 by reduction to simpler cases.  */
-      val = unary_complex_lvalue (code, arg);
-      if (val != 0)
-	return val;
-
-      switch (TREE_CODE (arg))
-	{
-	CASE_CONVERT:
-	case FLOAT_EXPR:
-	case FIX_TRUNC_EXPR:
-          /* Even if we're not being pedantic, we cannot allow this
-             extension when we're instantiating in a SFINAE
-             context.  */
-	  if (! lvalue_p (arg) && complain == tf_none)
-            {
-              if (complain & tf_error)
-                permerror (input_location, "ISO C++ forbids taking the address of a cast to a non-lvalue expression");
-              else
-                return error_mark_node;
-            }
-	  break;
-
-	case BASELINK:
-	  arg = BASELINK_FUNCTIONS (arg);
-	  /* Fall through.  */
-
-	case OVERLOAD:
-	  arg = OVL_CURRENT (arg);
-	  break;
-
-	case OFFSET_REF:
-	  /* Turn a reference to a non-static data member into a
-	     pointer-to-member.  */
-	  {
-	    tree type;
-	    tree t;
-
-	    if (!PTRMEM_OK_P (arg))
-	      return cp_build_unary_op (code, arg, 0, complain);
-
-	    t = TREE_OPERAND (arg, 1);
-	    if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE)
-	      {
-                if (complain & tf_error)
-                  error ("cannot create pointer to reference member %qD", t);
-		return error_mark_node;
-	      }
-
-	    type = build_ptrmem_type (context_for_name_lookup (t),
-				      TREE_TYPE (t));
-	    t = make_ptrmem_cst (type, TREE_OPERAND (arg, 1));
-	    return t;
-	  }
-
-	default:
-	  break;
-	}
-
-      /* Anything not already handled and not a true memory reference
-	 is an error.  */
-      if (TREE_CODE (argtype) != FUNCTION_TYPE
-	  && TREE_CODE (argtype) != METHOD_TYPE
-	  && TREE_CODE (arg) != OFFSET_REF
-	  && !lvalue_or_else (arg, lv_addressof, complain))
-	return error_mark_node;
-
-      if (argtype != error_mark_node)
-	argtype = build_pointer_type (argtype);
-
-      /* In a template, we are processing a non-dependent expression
-	 so we can just form an ADDR_EXPR with the correct type.  */
-      if (processing_template_decl || TREE_CODE (arg) != COMPONENT_REF)
-	{
-	  val = build_address (arg);
-	  if (TREE_CODE (arg) == OFFSET_REF)
-	    PTRMEM_OK_P (val) = PTRMEM_OK_P (arg);
-	}
-      else if (TREE_CODE (TREE_OPERAND (arg, 1)) == BASELINK)
-	{
-	  tree fn = BASELINK_FUNCTIONS (TREE_OPERAND (arg, 1));
-
-	  /* We can only get here with a single static member
-	     function.  */
-	  gcc_assert (TREE_CODE (fn) == FUNCTION_DECL
-		      && DECL_STATIC_FUNCTION_P (fn));
-	  mark_used (fn);
-	  val = build_address (fn);
-	  if (TREE_SIDE_EFFECTS (TREE_OPERAND (arg, 0)))
-	    /* Do not lose object's side effects.  */
-	    val = build2 (COMPOUND_EXPR, TREE_TYPE (val),
-			  TREE_OPERAND (arg, 0), val);
-	}
-      else if (DECL_C_BIT_FIELD (TREE_OPERAND (arg, 1)))
-	{
-          if (complain & tf_error)
-            error ("attempt to take address of bit-field structure member %qD",
-                   TREE_OPERAND (arg, 1));
-	  return error_mark_node;
-	}
-      else
-	{
-	  tree object = TREE_OPERAND (arg, 0);
-	  tree field = TREE_OPERAND (arg, 1);
-	  gcc_assert (same_type_ignoring_top_level_qualifiers_p
-		      (TREE_TYPE (object), decl_type_context (field)));
-	  val = build_address (arg);
-	}
-
-      if (TREE_CODE (argtype) == POINTER_TYPE
-	  && TREE_CODE (TREE_TYPE (argtype)) == METHOD_TYPE)
-	{
-	  build_ptrmemfunc_type (argtype);
-	  val = build_ptrmemfunc (argtype, val, 0,
-				  /*c_cast_p=*/false,
-				  tf_warning_or_error);
-	}
-
-      return val;
+      return cp_build_addr_expr (arg, complain);
 
     default:
       break;
@@ -6128,7 +6165,7 @@  build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
 	warning (0, "casting %qT to %qT does not dereference pointer",
 		 intype, type);
 
-      expr = cp_build_unary_op (ADDR_EXPR, expr, 0, complain);
+      expr = cp_build_addr_expr (expr, complain);
 
       if (warn_strict_aliasing > 2)
 	strict_aliasing_warning (TREE_TYPE (expr), type, expr);
@@ -6366,8 +6403,8 @@  build_const_cast_1 (tree dst_type, tree expr, bool complain,
 	}
       if (reference_type)
 	{
-	  expr = cp_build_unary_op (ADDR_EXPR, expr, 0, 
-                                    complain? tf_warning_or_error : tf_none);
+	  expr = cp_build_addr_expr (expr,
+				     complain ? tf_warning_or_error : tf_none);
 	  expr = build_nop (reference_type, expr);
 	  return convert_from_reference (expr);
 	}
@@ -8324,7 +8361,7 @@  non_reference (tree t)
 int
 lvalue_or_else (tree ref, enum lvalue_use use, tsubst_flags_t complain)
 {
-  int win = lvalue_p (ref);
+  int win = real_lvalue_p (ref);
 
   if (!win && (complain & tf_error))
     lvalue_error (use);
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-lvalue-req.C b/gcc/testsuite/g++.dg/cpp0x/rv-lvalue-req.C
new file mode 100644
index 0000000..ba1c306
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-lvalue-req.C
@@ -0,0 +1,12 @@ 
+// { dg-options -std=c++0x }
+
+template <class T> T&& declval();
+
+int main()
+{
+  &declval<int>();		        // { dg-error "lvalue" }
+  declval<int>() = declval<int>();	// { dg-error "lvalue" }
+  declval<int>()++;			// { dg-error "lvalue" }
+  --declval<int>();			// { dg-error "lvalue" }
+  declval<int>() += 1;			// { dg-error "lvalue" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/complit11.C b/gcc/testsuite/g++.dg/ext/complit11.C
index 7b41795..2cff6cd 100644
--- a/gcc/testsuite/g++.dg/ext/complit11.C
+++ b/gcc/testsuite/g++.dg/ext/complit11.C
@@ -6,7 +6,7 @@  struct A { int i; };
 template<int t>
 void foo()
 {
-    ((struct A) { 0 }).i += 1;
+    ((struct A) { 0 }).i += 1;	// { dg-error "lvalue required" }
 }
 
 void g(void)
diff --git a/gcc/testsuite/g++.old-deja/g++.law/temps1.C b/gcc/testsuite/g++.old-deja/g++.law/temps1.C
index cd0bc87..2e6a419 100644
--- a/gcc/testsuite/g++.old-deja/g++.law/temps1.C
+++ b/gcc/testsuite/g++.old-deja/g++.law/temps1.C
@@ -16,4 +16,4 @@  struct cookie
 };
 
 cookie cat(&foo("apabepa"));// { dg-warning "deprecated conversion" "dep" }
-// { dg-warning "taking address of temporary" "add" { target *-*-* } 18 }
+// { dg-error "lvalue required" "lvalue" { target *-*-* } 18 }
diff --git a/gcc/testsuite/g++.old-deja/g++.ns/koenig6.C b/gcc/testsuite/g++.old-deja/g++.ns/koenig6.C
index f77d975..b5fa9c9 100644
--- a/gcc/testsuite/g++.old-deja/g++.ns/koenig6.C
+++ b/gcc/testsuite/g++.old-deja/g++.ns/koenig6.C
@@ -2,12 +2,12 @@ 
 namespace A{
   struct X{};
 
-  X foo(X a){return a;}
+  X* foo(X a);
   void bar(X*){}
 }
 
 int main()
 {
   A::X x;
-  bar(&foo(x));  // { dg-warning "" } address of temporary
+  bar(foo(x));
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.oliva/partord1.C b/gcc/testsuite/g++.old-deja/g++.oliva/partord1.C
index 5559317..34fe92e 100644
--- a/gcc/testsuite/g++.old-deja/g++.oliva/partord1.C
+++ b/gcc/testsuite/g++.old-deja/g++.oliva/partord1.C
@@ -14,10 +14,10 @@  template <typename T> class bar {
 };
 
 template <typename T> void foo(T) {
-  bar<T>().i = 0; // ok, I'm a friend
+  bar<T>().i; // ok, I'm a friend
 }
 template <typename T> void foo(T*) {
-  bar<T*>().i = 1; // { dg-error "" } not a friend
+  bar<T*>().i; // { dg-error "" } not a friend
 }
 
 int main() {