diff mbox

[C/C++] Fix PR c++/50608

Message ID 201110131548.12325.ebotcazou@adacore.com
State New
Headers show

Commit Message

Eric Botcazou Oct. 13, 2011, 1:48 p.m. UTC
Hi,

this is a regression present on the mainline and 4.6 branch, introduced by the 
offsetof folding change.  The compiler now rejects:

int fails = __builtin_offsetof (C, b.offset);

error: cannot apply 'offsetof' to a non constant address

whereas it still accepts:

int works = (int)(&(((C*)0)->b.offset));


The problem is a lack of associativity of the offsetof folding: the first case 
is built as

  offsetof (offsetof (C.b), offset))

whereas the second is built as

  offsetof (C, b.offset)

so, in the first case, the second offsetof has a non-NULL base, which triggers 
the error about the non-constant address.

It turns out that fold_offsetof_1 already has the code to handle a non-null 
base, but it is unused because of the integer_zerop test.  The proposed fix is 
therefore to remove the limitation, which in turn makes it possible to factor 
out the common handling of a non-null base and finally to get rid of the 
STOP_REF argument.

Bootstrapped/regtested on x86_64-suse-linux, OK for mainline and 4.6 branch?


2011-10-13  Eric Botcazou  <ebotcazou@adacore.com>

	PR c++/50608
	* c-parser.c (c_parser_postfix_expression) <RID_OFFSETOF>: Adjust call
	to fold_offsetof.
	* c-typeck.c (build_unary_op) <ADDR_EXPR>: Call fold_offsetof_1.
c-family/
	* c-common.c (c_fully_fold_internal) <ADDR_EXPR>: Call fold_offsetof_1.
	(fold_offsetof_1): Make global.  Remove STOP_REF argument and adjust.
	<INDIRECT_REF>: Return the argument.
	<ARRAY_REF>: Remove special code for negative offset.
	Call fold_build_pointer_plus instead of size_binop.
	(fold_offsetof): Remove STOP_REF argument and adjust.
	* c-common.h (fold_offsetof_1): Declare.
	(fold_offsetof): Remove STOP_REF argument.
cp/
	* semantics.c (finish_offsetof): Adjust call to fold_offsetof.
	* typeck.c (cp_build_addr_expr_1): Call fold_offsetof_1.


2011-10-13  Eric Botcazou  <ebotcazou@adacore.com>

	* g++.dg/other/offsetof7.C: New test.

Comments

Jason Merrill Nov. 4, 2011, 2:39 p.m. UTC | #1
OK.

Jason
diff mbox

Patch

Index: c-parser.c
===================================================================
--- c-parser.c	(revision 179844)
+++ c-parser.c	(working copy)
@@ -6388,7 +6388,7 @@  c_parser_postfix_expression (c_parser *p
 	      c_parser_error (parser, "expected identifier");
 	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
 				       "expected %<)%>");
-	    expr.value = fold_offsetof (offsetof_ref, NULL_TREE);
+	    expr.value = fold_offsetof (offsetof_ref);
 	  }
 	  break;
 	case RID_CHOOSE_EXPR:
Index: c-typeck.c
===================================================================
--- c-typeck.c	(revision 179844)
+++ c-typeck.c	(working copy)
@@ -3890,10 +3890,7 @@  build_unary_op (location_t location,
       if (val && TREE_CODE (val) == INDIRECT_REF
           && TREE_CONSTANT (TREE_OPERAND (val, 0)))
 	{
-	  tree op0 = fold_offsetof (arg, val), op1;
-
-	  op1 = fold_convert_loc (location, argtype, TREE_OPERAND (val, 0));
-	  ret = fold_build_pointer_plus_loc (location, op1, op0);
+	  ret = fold_convert_loc (location, argtype, fold_offsetof_1 (arg));
 	  goto return_build_unary_op;
 	}
 
Index: c-family/c-common.c
===================================================================
--- c-family/c-common.c	(revision 179844)
+++ c-family/c-common.c	(working copy)
@@ -1272,12 +1272,7 @@  c_fully_fold_internal (tree expr, bool i
 	  && (op1 = get_base_address (op0)) != NULL_TREE
 	  && TREE_CODE (op1) == INDIRECT_REF
 	  && TREE_CONSTANT (TREE_OPERAND (op1, 0)))
-	{
-	  tree offset = fold_offsetof (op0, op1);
-	  op1
-	    = fold_convert_loc (loc, TREE_TYPE (expr), TREE_OPERAND (op1, 0));
-	  ret = fold_build_pointer_plus_loc (loc, op1, offset);
-	}
+	ret = fold_convert_loc (loc, TREE_TYPE (expr), fold_offsetof_1 (op0));
       else if (op0 != orig_op0 || in_init)
 	ret = in_init
 	  ? fold_build1_initializer_loc (loc, code, TREE_TYPE (expr), op0)
@@ -8547,20 +8542,15 @@  c_common_to_target_charset (HOST_WIDE_IN
     return uc;
 }
 
-/* Build the result of __builtin_offsetof.  EXPR is a nested sequence of
-   component references, with STOP_REF, or alternatively an INDIRECT_REF of
-   NULL, at the bottom; much like the traditional rendering of offsetof as a
-   macro.  Returns the folded and properly cast result.  */
+/* Fold an offsetof-like expression.  EXPR is a nested sequence of component
+   references with an INDIRECT_REF of a constant at the bottom; much like the
+   traditional rendering of offsetof as a macro.  Return the folded result.  */
 
-static tree
-fold_offsetof_1 (tree expr, tree stop_ref)
+tree
+fold_offsetof_1 (tree expr)
 {
-  enum tree_code code = PLUS_EXPR;
   tree base, off, t;
 
-  if (expr == stop_ref && TREE_CODE (expr) != ERROR_MARK)
-    return size_zero_node;
-
   switch (TREE_CODE (expr))
     {
     case ERROR_MARK:
@@ -8577,15 +8567,15 @@  fold_offsetof_1 (tree expr, tree stop_re
 
     case NOP_EXPR:
     case INDIRECT_REF:
-      if (!integer_zerop (TREE_OPERAND (expr, 0)))
+      if (!TREE_CONSTANT (TREE_OPERAND (expr, 0)))
 	{
 	  error ("cannot apply %<offsetof%> to a non constant address");
 	  return error_mark_node;
 	}
-      return size_zero_node;
+      return TREE_OPERAND (expr, 0);
 
     case COMPONENT_REF:
-      base = fold_offsetof_1 (TREE_OPERAND (expr, 0), stop_ref);
+      base = fold_offsetof_1 (TREE_OPERAND (expr, 0));
       if (base == error_mark_node)
 	return base;
 
@@ -8603,21 +8593,14 @@  fold_offsetof_1 (tree expr, tree stop_re
       break;
 
     case ARRAY_REF:
-      base = fold_offsetof_1 (TREE_OPERAND (expr, 0), stop_ref);
+      base = fold_offsetof_1 (TREE_OPERAND (expr, 0));
       if (base == error_mark_node)
 	return base;
 
       t = TREE_OPERAND (expr, 1);
-      if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0)
-	{
-	  code = MINUS_EXPR;
-	  t = fold_build1_loc (input_location, NEGATE_EXPR, TREE_TYPE (t), t);
-	}
-      t = convert (sizetype, t);
-      off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t);
 
       /* Check if the offset goes beyond the upper bound of the array.  */
-      if (code == PLUS_EXPR && TREE_CODE (t) == INTEGER_CST)
+      if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) >= 0)
 	{
 	  tree upbound = array_ref_up_bound (expr);
 	  if (upbound != NULL_TREE
@@ -8657,26 +8640,30 @@  fold_offsetof_1 (tree expr, tree stop_re
 		}
 	    }
 	}
+
+      t = convert (sizetype, t);
+      off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t);
       break;
 
     case COMPOUND_EXPR:
       /* Handle static members of volatile structs.  */
       t = TREE_OPERAND (expr, 1);
       gcc_assert (TREE_CODE (t) == VAR_DECL);
-      return fold_offsetof_1 (t, stop_ref);
+      return fold_offsetof_1 (t);
 
     default:
       gcc_unreachable ();
     }
 
-  return size_binop (code, base, off);
+  return fold_build_pointer_plus (base, off);
 }
 
+/* Likewise, but convert it to the return type of offsetof.  */
+
 tree
-fold_offsetof (tree expr, tree stop_ref)
+fold_offsetof (tree expr)
 {
-  /* Convert back from the internal sizetype to size_t.  */
-  return convert (size_type_node, fold_offsetof_1 (expr, stop_ref));
+  return convert (size_type_node, fold_offsetof_1 (expr));
 }
 
 /* Warn for A ?: C expressions (with B omitted) where A is a boolean 
Index: c-family/c-common.h
===================================================================
--- c-family/c-common.h	(revision 179844)
+++ c-family/c-common.h	(working copy)
@@ -954,7 +954,8 @@  extern bool c_dump_tree (void *, tree);
 
 extern void verify_sequence_points (tree);
 
-extern tree fold_offsetof (tree, tree);
+extern tree fold_offsetof_1 (tree);
+extern tree fold_offsetof (tree);
 
 /* Places where an lvalue, or modifiable lvalue, may be required.
    Used to select diagnostic messages in lvalue_error and
Index: cp/typeck.c
===================================================================
--- cp/typeck.c	(revision 179844)
+++ cp/typeck.c	(working copy)
@@ -4849,9 +4849,7 @@  cp_build_addr_expr_1 (tree arg, bool str
       && TREE_CONSTANT (TREE_OPERAND (val, 0)))
     {
       tree type = build_pointer_type (argtype);
-      tree op0 = fold_convert (type, TREE_OPERAND (val, 0));
-      tree op1 = fold_offsetof (arg, val);
-      return fold_build_pointer_plus (op0, op1);
+      return fold_convert (type, fold_offsetof_1 (arg));
     }
 
   /* Handle complex lvalues (when permitted)
Index: cp/semantics.c
===================================================================
--- cp/semantics.c	(revision 179844)
+++ cp/semantics.c	(working copy)
@@ -3424,7 +3424,7 @@  finish_offsetof (tree expr)
       if (!complete_type_or_else (TREE_TYPE (object), object))
 	return error_mark_node;
     }
-  return fold_offsetof (expr, NULL_TREE);
+  return fold_offsetof (expr);
 }
 
 /* Replace the AGGR_INIT_EXPR at *TP with an equivalent CALL_EXPR.  This