This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

C++ PATCH: expressions in void context


Hi,
attached is a patch which fixes
http://egcs.cygnus.com/ml/gcc-bugs/1999-06/msg00468.html and implements
the final sentance of 5.2.9/4
(http://egcs.cygnus.com/ml/gcc-bugs/1999-06/msg00493.html gives an
explanation)

The patch renames require_complete_type_in_void to check_expr_in_void.
And this then
makes sure that no implicit dereference happens. If an lvalue object is
accessed, the access is removed (no rvalue decay).
cplus_expand_stmt (decl.c) only calls check_expr_in_void when dealing
with full statements (otherwise the result value of incomplete
statements is removed). It is also unnecessary for unused reference
values to be stripped here, because check_expr_in_void does that.

Should the accessed object have volatile type, and extra_warnings
enabled, a warning is issued about the volatile not being accessed.

The patch amends g++bug/90428_01.C to test for the correct behaviour of
5.2.9/4. Please note that this means two cases in there now start
failing (lines 32 and 36) due to a problem with conditional exprs. A
separate patch for that problem is on its way.

The attached test case makes sure that code generation does not occur,
by doing things like `*(volatile int *)0', which would segfault
otherwise.

Ok?

nathan

-- 
Dr Nathan Sidwell :: Computer Science Department :: Bristol University
        I have seen the death of PhotoShop -- it is called GIMP
nathan@acm.org  http://www.cs.bris.ac.uk/~nathan/  nathan@cs.bris.ac.uk
1999-08-27  Nathan Sidwell  <nathan@acm.org>

	* cp-tree.h (require_complete_type_in_void): Rename to ...
	(check_expr_in_void): ... here.
	* typeck.c (require_complete_type_in_void): Rename to ...
	(check_expr_in_void): ... here. Don't check incompleteness. Do
	check volatileness, prevent access to volatile objects.
	(build_compound_expr): Adjust.
	(build_static_cast): Call check_expr_in_void.
	(build_c_cast): Adjust.
	* cvt.c (ocp_convert): Adjust.
	* decl.c (cplus_expand_expr_stmt): Only check_expr_in_void when
	dealing with complete expressions.  No need to strip
	INDIRECT_REFs.

Index: gcc/cp/cp-tree.h
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.271
diff -c -3 -p -r1.271 cp-tree.h
*** cp-tree.h	1999/08/25 22:07:01	1.271
--- cp-tree.h	1999/08/27 08:07:26
*************** extern int string_conv_p			PROTO((tree, 
*** 3504,3510 ****
  extern tree condition_conversion		PROTO((tree));
  extern tree target_type				PROTO((tree));
  extern tree require_complete_type		PROTO((tree));
! extern tree require_complete_type_in_void	PROTO((tree));
  extern tree complete_type			PROTO((tree));
  extern tree complete_type_or_else               PROTO((tree, tree));
  extern int type_unknown_p			PROTO((tree));
--- 3504,3510 ----
  extern tree condition_conversion		PROTO((tree));
  extern tree target_type				PROTO((tree));
  extern tree require_complete_type		PROTO((tree));
! extern tree check_expr_in_void			PROTO((tree));
  extern tree complete_type			PROTO((tree));
  extern tree complete_type_or_else               PROTO((tree, tree));
  extern int type_unknown_p			PROTO((tree));
Index: gcc/cp/typeck.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/typeck.c,v
retrieving revision 1.198
diff -c -3 -p -r1.198 typeck.c
*** typeck.c	1999/08/25 22:07:16	1.198
--- typeck.c	1999/08/27 08:07:44
*************** require_complete_type (value)
*** 129,142 ****
      return error_mark_node;
  }
  
! /* Makes sure EXPR is a complete type when used in a void context, like a
!    whole expression, or lhs of a comma operator. Issue a diagnostic and
!    return error_mark_node on failure. This is a little tricky, because some
!    valid void types look stunningly similar to invalid void types. We err on
!    the side of caution */
  
  tree
! require_complete_type_in_void (expr)
       tree expr;
  {
    switch (TREE_CODE (expr))
--- 129,147 ----
      return error_mark_node;
  }
  
! /* When an expression is used in a void context, it's value is discarded
!    and no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
!    stmt.expr/1, expr.comma/1].  This means that `*pvi' where pvi is
!    `volatile int *' does NOT result in a dereference.  We have to remove the
!    dereference in these cases, so that code generation does not emit a
!    volatile access.
!    Similarly there is no requirement that the expression have complete type,
!    or reference an object -- both `*(void *)e' and `*(int *)0' are valid. 
!    Because this makes a difference when the object is volatile, we issue a
!    warning in that case.  */
  
  tree
! check_expr_in_void (expr)
       tree expr;
  {
    switch (TREE_CODE (expr))
*************** require_complete_type_in_void (expr)
*** 146,152 ****
          tree op;
          
          op = TREE_OPERAND (expr,2);
!         op = require_complete_type_in_void (op);
          TREE_OPERAND (expr,2) = op;
          if (op == error_mark_node)
            {
--- 151,157 ----
          tree op;
          
          op = TREE_OPERAND (expr,2);
!         op = check_expr_in_void (op);
          TREE_OPERAND (expr,2) = op;
          if (op == error_mark_node)
            {
*************** require_complete_type_in_void (expr)
*** 162,168 ****
          tree op;
          
          op = TREE_OPERAND (expr,1);
!         op = require_complete_type_in_void (op);
          TREE_OPERAND (expr,1) = op;
          if (op == error_mark_node)
            {
--- 167,173 ----
          tree op;
          
          op = TREE_OPERAND (expr,1);
!         op = check_expr_in_void (op);
          TREE_OPERAND (expr,1) = op;
          if (op == error_mark_node)
            {
*************** require_complete_type_in_void (expr)
*** 175,223 ****
      
      case NON_LVALUE_EXPR:
      case NOP_EXPR:
!       {
!         tree op;
!         
!         op = TREE_OPERAND (expr,0);
!         op = require_complete_type_in_void (op);
!         TREE_OPERAND (expr,0) = op;
!         if (op == error_mark_node)
!           {
!             expr = op;
!             break;
!           }
!         break;
!       }
      
-     case CALL_EXPR:   /* function call return can be ignored */
      case RTL_EXPR:    /* RTL nodes have no value */
      case DELETE_EXPR: /* delete expressions have no type */
      case VEC_DELETE_EXPR:
-     case INTEGER_CST: /* used for null pointer */
      case EXIT_EXPR:   /* have no return */
      case LOOP_EXPR:   /* have no return */
      case BIND_EXPR:   /* have no return */
      case STMT_EXPR: /* have no return */
      case THROW_EXPR:  /* have no return */
!     case MODIFY_EXPR: /* sometimes this has a void type, but that's ok */
!     case CONVERT_EXPR:  /* sometimes has a void type */
        break;
      
-     case INDIRECT_REF:
-       {
-         tree op = TREE_OPERAND (expr,0);
-         
-         /* Calling a function returning a reference has an implicit
-            dereference applied. We don't want to make that an error. */
-         if (TREE_CODE (op) == CALL_EXPR
-             && TREE_CODE (TREE_TYPE (op)) == REFERENCE_TYPE)
-           break;
-         /* else fallthrough */
-       }
-     
      default:
!       expr = require_complete_type (expr);
!       break;
      }
  
    return expr;
--- 180,208 ----
      
      case NON_LVALUE_EXPR:
      case NOP_EXPR:
!       /* These have already decayed to rvalue and checked for completeness. */
!       break;
      
      case RTL_EXPR:    /* RTL nodes have no value */
      case DELETE_EXPR: /* delete expressions have no type */
      case VEC_DELETE_EXPR:
      case EXIT_EXPR:   /* have no return */
      case LOOP_EXPR:   /* have no return */
      case BIND_EXPR:   /* have no return */
      case STMT_EXPR: /* have no return */
      case THROW_EXPR:  /* have no return */
!     case MODIFY_EXPR: /* ok */
!     case CALL_EXPR:   /* we have a special meaning for volatile void fn() */
        break;
      
      default:
!       if (extra_warnings && TREE_TYPE (expr) && TYPE_VOLATILE (TREE_TYPE (expr)))
!         cp_warning ("volatile object in void context will not be accessed");
!       /* Make sure the object is not accessed.  */
!       if (TREE_CODE (expr) == INDIRECT_REF)
!         expr = TREE_OPERAND (expr,0);
!       else if (TREE_CODE (expr) == VAR_DECL || TREE_CODE (expr) == PARM_DECL)
!         expr = void_zero_node;
      }
  
    return expr;
*************** build_compound_expr (list)
*** 5229,5235 ****
      }
  
    first = TREE_VALUE (list);
!   first = require_complete_type_in_void (first);
    if (first == error_mark_node)
      return error_mark_node;
    
--- 5234,5240 ----
      }
  
    first = TREE_VALUE (list);
!   first = check_expr_in_void (first);
    if (first == error_mark_node)
      return error_mark_node;
    
*************** build_static_cast (type, expr)
*** 5273,5279 ****
      expr = TREE_OPERAND (expr, 0);
  
    if (TREE_CODE (type) == VOID_TYPE)
!     return build1 (CONVERT_EXPR, type, expr);
  
    if (TREE_CODE (type) == REFERENCE_TYPE)
      return (convert_from_reference
--- 5278,5287 ----
      expr = TREE_OPERAND (expr, 0);
  
    if (TREE_CODE (type) == VOID_TYPE)
!     {
!       expr = check_expr_in_void (expr);
!       return build1 (CONVERT_EXPR, type, expr);
!     }
  
    if (TREE_CODE (type) == REFERENCE_TYPE)
      return (convert_from_reference
*************** build_c_cast (type, expr)
*** 5633,5639 ****
  
    if (TREE_CODE (type) == VOID_TYPE)
      {
!       value = require_complete_type_in_void (value);
        if (value != error_mark_node)
          value = build1 (CONVERT_EXPR, void_type_node, value);
      }
--- 5641,5647 ----
  
    if (TREE_CODE (type) == VOID_TYPE)
      {
!       value = check_expr_in_void (value);
        if (value != error_mark_node)
          value = build1 (CONVERT_EXPR, void_type_node, value);
      }
Index: gcc/cp/cvt.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/cvt.c,v
retrieving revision 1.63
diff -c -3 -p -r1.63 cvt.c
*** cvt.c	1999/08/11 20:22:25	1.63
--- cvt.c	1999/08/27 08:07:27
*************** ocp_convert (type, expr, convtype, flags
*** 689,695 ****
  
    if (code == VOID_TYPE && (convtype & CONV_STATIC))
      {
!       e = require_complete_type_in_void (e);
        if (e != error_mark_node)
          e = build1 (CONVERT_EXPR, void_type_node, e);
  
--- 689,695 ----
  
    if (code == VOID_TYPE && (convtype & CONV_STATIC))
      {
!       e = check_expr_in_void (e);
        if (e != error_mark_node)
          e = build1 (CONVERT_EXPR, void_type_node, e);
  
Index: gcc/cp/decl.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/decl.c,v
retrieving revision 1.415
diff -c -3 -p -r1.415 decl.c
*** decl.c	1999/08/26 04:19:51	1.415
--- decl.c	1999/08/27 08:07:37
*************** void
*** 14272,14278 ****
  cplus_expand_expr_stmt (exp)
       tree exp;
  {
!   exp = require_complete_type_in_void (exp);
    
    if (TREE_CODE (exp) == FUNCTION_DECL)
      {
--- 14272,14279 ----
  cplus_expand_expr_stmt (exp)
       tree exp;
  {
!   if (stmts_are_full_exprs_p)
!     exp = check_expr_in_void (exp);
    
    if (TREE_CODE (exp) == FUNCTION_DECL)
      {
*************** cplus_expand_expr_stmt (exp)
*** 14285,14295 ****
       libg++ to miscompile, and tString to core dump.  */
    exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
  #endif
- 
-   /* Strip unused implicit INDIRECT_REFs of references.  */
-   if (TREE_CODE (exp) == INDIRECT_REF
-       && TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == REFERENCE_TYPE)
-     exp = TREE_OPERAND (exp, 0);
  
    /* If we don't do this, we end up down inside expand_expr
       trying to do TYPE_MODE on the ERROR_MARK, and really
--- 14286,14291 ----
Index: gcc/testsuite/g++.old-deja/g++.bugs/900428_01.C
===================================================================
RCS file: /cvs/egcs/egcs/gcc/testsuite/g++.old-deja/g++.bugs/900428_01.C,v
retrieving revision 1.3
diff -c -3 -p -r1.3 900428_01.C
*** 900428_01.C	1999/02/28 00:22:38	1.3
--- 900428_01.C	1999/08/27 08:07:44
***************
*** 1,4 ****
--- 1,5 ----
  // g++ 1.37.1 bug 900428_01
+ // Special g++ Options: -W -ansi -pedantic
  
  // g++ fails to issue error messages for cases where an incomplete type
  // object must be evaluated if the value of such an evaluation is not
***************
*** 10,15 ****
--- 11,20 ----
  // because the abstract semantics seem to require the evaluation of such
  // values whether they are volatile or not.
  
+ // [expr.static.cast/4, stmt.expr/1, expr.comma/1] show that expressions do
+ // not under go lvalue to rvalue decay, unless the value is actually used. This
+ // can be surprising when the object is volatile.
+ 
  // keywords: incomplete types, evaluation, volatile qualifier
  // Build don't link: 
  
*************** int i;
*** 17,51 ****
  
  void *pv;
  volatile void *pvv;
! struct s;               // ERROR - forward declaration
! extern struct s es, *ps;  // ERROR - defined here
! extern volatile struct s evs, *pvs; // ERROR - defined here
  
  void pv_test ()
  {
!   *pv;			// ERROR - invalid void
!   (i ? *pv : *pv);	// ERROR - invalid void
!   *pv, *pv;		// ERROR - invalid void
! 
!   *pvv;			// ERROR - invalid void
!   (i ? *pvv : *pvv);	// ERROR - invalid void
!   *pvv, *pvv;		// ERROR - invalid void
! 
!   es;			// ERROR - incomplete
!   (i ? es : es);	// ERROR - undefined type
!   es, es;		// ERROR - incomplete
! 
!   evs;			// ERROR - incomplete
!   (i ? evs : evs);	// ERROR - undefined type
!   evs, evs;		// ERROR - incomplete
! 
!   *ps;			// ERROR - undefined type
!   (i ? *ps : *ps);	// ERROR - undefined type
!   *ps, *ps;		// ERROR - undefined type
! 
!   *pvs;			// ERROR - undefined type
!   (i ? *pvs : *pvs);	// ERROR - undefined type
!   *pvs, *pvs;		// ERROR - undefined type
  }
  
  int main () { return 0; }
--- 22,56 ----
  
  void *pv;
  volatile void *pvv;
! struct s;
! extern struct s es, *ps;
! extern volatile struct s evs, *pvs;
  
  void pv_test ()
  {
!   (void)*pv;			// ok, no warning
!   (void)(i ? *pv : *pv);	// ERROR - dereference void *
!   (void)((void)*pv, *pv);	// ok, no warning
! 
!   (void)*pvv;			// WARNING - volatile not accessed
!   (void)(i ? *pvv : *pvv);	// ERROR - dereference void *
!   (void)((void)*pvv, *pvv);	// WARNING - volatile not accessed
! 
!   (void)es;			// ok
!   (void)(i ? es : es);		// ok
!   (void)((void)es, es);		// ok
! 
!   (void)evs;			// WARNING - volatile not accessed
!   (void)(i ? evs : evs);	// WARNING - volatile not accessed
!   (void)((void)evs, evs);	// WARNING - volatile not accessed
! 
!   (void)*ps;			// ok
!   (void)(i ? *ps : *ps);	// ok
!   (void)((void)*ps, *ps);	// ok
! 
!   (void)*pvs;			// WARNING - volatile not accessed
!   (void)(i ? *pvs : *pvs);	// WARNING - volatile not accessed
!   (void)((void)*pvs, *pvs);	// WARNING - volatile not accessed
  }
  
  int main () { return 0; }
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 24 Aug 1999 <nathan@acm.org>

// lvalues in void contexts do not decay, so volatiles are not referenced.
// Make sure we DTRT

volatile int &fnr(volatile int *p) {return *p;}
volatile int *fnp(volatile int *p) {return p;}
struct S {int t;};
extern volatile S s; // never defined, so we get a link error if used.

void f (int i, volatile int *ip, volatile int &ir)
{
  *(void *)0;
  *ip;            
  *ip, *ip;       
  i ? *ip : *ip;  
  ir;             
  ir, ir;         
  i ? ir : ir;    
  fnr(ip);        
  *fnp(ip);       
  s;              
}

int main()
{
  // although passing NULL, we never actually dereference it.
  // if we do dereference it, we'll seg fault
  f(0, 0, *(int *)0);
  return 0;
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]