C++ PATCH: expressions in void context

Nathan Sidwell nathan@acm.org
Tue Sep 7 02:39:00 GMT 1999


Hi,
Here's a patch for the recently discussed problem about expressions in
void context. See http://egcs.cygnus.com/ml/gcc-patches/1999-09/msg00001.html
for the context (pun intended).

* Remove require_complete_type_in_void. That function was erroneously
rejecting valid programs.
* Insert convert_to_void (cvt.c). This new function is responsible for all
void conversions (explicit and implicit).
* Allow dereferencing an incomplete type in such a context.
* Warn on dereferencing an incomplete volatile.
* Treat dereferencing a complete volatile as a read.
* Warn on using a volatile reference in such contexts (complete or not)
* Do not treat a volatile reference as a read.
* Add documentation to extend.texi describing this behaviour.
* Pay attention to 13.4 which further restricts where an overloaded function
name can be used without arguments (i.e. not here)
* Patch various test cases which violated the previous
* Patch 900428_01.C to check we do what we intended wrt completeness & volatiles
* New testcase which deals with the 13.4 issue

The new testcase still FAILS in three places,
  FAIL: aaa/nathan56.C not a call (test for warnings, line 83)
  FAIL: aaa/nathan56.C (test for excess errors)
  /home/nathan/egcs/void/egcs/gcc/testsuite/g++.old-deja/aaa/nathan56.C:55: argument of type `int' does not match `void (*)(int)'
  /home/nathan/egcs/void/egcs/gcc/testsuite/g++.old-deja/aaa/nathan56.C:56: argument of type `int' does not match `void (*)(int)'

these appears to be due to bugs in build_x_compound_expr and build_compound_expr.
I've introduced a couple of FIXME comments indicating where the problem appears
to be. These seem like latent bugs which are now more visible with
convert_to_void. There were a couple of problems with the expression statement
extension, ({...}), where the statements are not in a void context, as the
result might be used.

The new testcases also produces a lot of sorrys from the error printing
routines -- I'll attend to that later.

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-09-06  Nathan Sidwell  <nathan@acm.org>

	* extend.texi (Volatiles): New node.

1999-09-06  Nathan Sidwell  <nathan@acm.org>

	* cp-tree.h (convert_to_void): Prototype new function.
	(require_complete_type_in_void): Remove prototype.
	* cvt.c (convert_to_void): New function.
	(ocp_convert): Use convert_to_void.
	* decl.c (cplus_expand_expr_stmt): Likewise, for complete
	expressions.
	* typeck.c (require_complete_type_in_void): Remove function.
	(build_compound_expr): Use convert_to_void.
	(build_static_cast): Likewise.
	(build_c_cast): Likewise.
	* semantics.c (finish_expr_stmt): Do not decay full expressions.
	
	* typeck.c (build_x_compound_expr): Add FIXME.

Index: gcc/extend.texi
===================================================================
RCS file: /cvs/egcs/egcs/gcc/extend.texi,v
retrieving revision 1.31
diff -c -3 -p -r1.31 extend.texi
*** extend.texi	1999/08/13 07:48:44	1.31
--- extend.texi	1999/09/07 08:45:42
*************** Predefined Macros,cpp.info,The C Preproc
*** 3150,3155 ****
--- 3150,3156 ----
  @menu
  * Naming Results::      Giving a name to C++ function return values.
  * Min and Max::		C++ Minimum and maximum operators.
+ * Volatiles::		What constitutes an access to a volatile object.
  * C++ Interface::       You can use a single C++ header file for both
                           declarations and definitions.
  * Template Instantiation:: Methods for ensuring that exactly one copy of
*************** write @w{@samp{int min = i <? j;}} inste
*** 3315,3320 ****
--- 3316,3402 ----
  Since @code{<?} and @code{>?} are built into the compiler, they properly
  handle expressions with side-effects;  @w{@samp{int min = i++ <? j++;}}
  works correctly.
+ 
+ @node Volatiles
+ @section When is a Volatile Object Accessed?
+ @cindex accessing volatiles
+ @cindex volatile read
+ @cindex volatile write
+ @cindex volatile access
+ 
+ Both the C and C++ standard have the concept of volatile objects. These
+ are normally accessed by pointers and used for accessing hardware. The
+ standards encourage compilers to refrain from optimizations on
+ concerning accesses to volatile objects that it might perform on
+ non-volatile objects. The C standard leaves it implementation defined
+ as to what constitutes a volatile access. The C++ standard omits to
+ specify this, except to say that C++ should behave in a similar manner
+ to C with respect to volatiles, where possible. The minimum either
+ standard specifies is that at a sequence point all previous access to
+ volatile objects have stabilized and no subsequent accesses have
+ occurred. Thus an implementation is free to reorder and combine
+ volatile accesses which occur between sequence points, but cannot do so
+ for accesses across a sequence point. The use of volatiles does not
+ allow you to violate the restriction on updating objects multiple times
+ within a sequence point.
+ 
+ In most expressions, it is intuitively obvious what is a read and what is
+ a write. For instance
+ 
+ @example
+ volatile int *dst = <somevalue>;
+ volatile int *src = <someothervalue>;
+ *dst = *src;
+ @end example
+ 
+ @noindent
+ will cause a read of the volatile object pointed to by @var{src} and stores the
+ value into the volatile object pointed to by @var{dst}. There is no
+ guarantee that these reads and writes are atomic, especially for objects
+ larger than @code{int}.
+ 
+ Less obvious expressions are where something which looks like an access
+ is used in a void context. An example would be,
+ 
+ @example
+ volatile int *src = <somevalue>;
+ *src;
+ @end example
+ 
+ With C, such expressions are rvalues, and as rvalues cause a read of
+ the object, gcc interprets this as a read of the volatile being pointed
+ to. The C++ standard specifies that such expressions do not undergo
+ lvalue to rvalue conversion, and that the type of the dereferenced
+ object may be incomplete. The C++ standard does not specify explicitly
+ that it is this lvalue to rvalue conversion which is responsible for
+ causing an access. However, there is reason to believe that it is,
+ because otherwise certain simple expressions become undefined. However,
+ because it would surprise most programmers, g++ treats dereferencing a
+ pointer to volatile object of complete type in a void context as a read
+ of the object. When the object has incomplete type, g++ issues a
+ warning.
+ 
+ @example
+ struct S;
+ struct T @{int m;@};
+ volatile S *ptr1 = <somevalue>;
+ volatile T *ptr2 = <somevalue>;
+ *ptr1;
+ *ptr2;
+ @end example
+ 
+ In this example, a warning is issued for @code{*ptr1}, and @code{*ptr2}
+ causes a read of the object pointed to. If you wish to force an error on
+ the first case, you must force a conversion to rvalue with, for instance
+ a static cast, @code{static_cast<S>(*ptr1)}.
+ 
+ When using a reference to volatile, g++ does not treat equivalent
+ expressions as accesses to volatiles, but instead issues a warning that
+ no volatile is accessed. The rationale for this is that otherwise it
+ becomes difficult to determine where volatile access occur, and not
+ possible to ignore the return value from functions returning volatile
+ references. Again, if you wish to force a read, cast the reference to
+ an rvalue.
  
  @node C++ Interface
  @section Declarations and Definitions in One Header
Index: gcc/cp/cp-tree.h
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.286
diff -c -3 -p -r1.286 cp-tree.h
*** cp-tree.h	1999/09/06 02:43:05	1.286
--- cp-tree.h	1999/09/06 12:34:53
*************** extern tree convert_pointer_to_real		PRO
*** 3054,3059 ****
--- 3054,3060 ----
  extern tree convert_pointer_to			PROTO((tree, tree));
  extern tree ocp_convert				PROTO((tree, tree, int, int));
  extern tree cp_convert				PROTO((tree, tree));
+ extern tree convert_to_void			PROTO((tree, const char */*implicit context*/));
  extern tree convert				PROTO((tree, tree));
  extern tree convert_force			PROTO((tree, tree, int));
  extern tree build_type_conversion		PROTO((tree, tree, int));
*************** extern int string_conv_p			PROTO((tree, 
*** 3732,3738 ****
  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));
--- 3733,3738 ----
Index: gcc/cp/cvt.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/cvt.c,v
retrieving revision 1.64
diff -c -3 -p -r1.64 cvt.c
*** cvt.c	1999/08/30 18:54:20	1.64
--- cvt.c	1999/09/06 12:34:54
*************** ocp_convert (type, expr, convtype, flags
*** 715,724 ****
  
    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);
! 
        return e;
      }
  
--- 715,721 ----
  
    if (code == VOID_TYPE && (convtype & CONV_STATIC))
      {
!       e = convert_to_void (e, /*implicit=*/NULL);
        return e;
      }
  
*************** ocp_convert (type, expr, convtype, flags
*** 854,859 ****
--- 851,992 ----
    if (flags & LOOKUP_SPECULATIVELY)
      return NULL_TREE;
    return error_mark_node;
+ }
+ 
+ /* When an expression is used in a void context, its value is discarded and
+    no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
+    stmt.expr/1, expr.comma/1].  This permits dereferencing an incomplete type
+    in a void context. The C++ standard does not define what an `access' to an
+    object is, but there is reason to beleive that it is the lvalue to rvalue
+    conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it
+    accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8
+    indicates that volatile semantics should be the same between C and C++
+    where ever possible. C leaves it implementation defined as to what
+    constitutes an access to a volatile. So, we interpret `*vp' as a read of
+    the volatile object `vp' points to, unless that is an incomplete type. For
+    volatile references we do not do this interpretation, because that would
+    make it impossible to ignore the reference return value from functions. We
+    issue warnings in the confusing cases.
+    
+    IMPLICIT is tells us the context of an implicit void conversion.  */
+ 
+ tree
+ convert_to_void (expr, implicit)
+      tree expr;
+      const char *implicit;
+ {
+   tree op = expr;
+   
+   if (expr == error_mark_node)
+     return expr;
+   if (!TREE_TYPE (expr))
+     return expr;
+   if (same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (expr)), void_type_node))
+     return expr;
+   switch (TREE_CODE (expr))
+     {
+     case COND_EXPR:
+       {
+         /* The two parts of a cond expr might be separate lvalues.  */
+         tree op1 = TREE_OPERAND (expr,1);
+         tree op2 = TREE_OPERAND (expr,2);
+         tree new_op1 = convert_to_void (op1, implicit);
+         tree new_op2 = convert_to_void (op2, implicit);
+         
+         if (new_op1 != op1 || new_op2 != op2)
+           expr = build (COND_EXPR,
+                         implicit ? TREE_TYPE (expr) : void_type_node,
+                         TREE_OPERAND (expr, 0), new_op1, new_op2);
+         break;
+       }
+     
+     case COMPOUND_EXPR:
+       {
+         /* The second part of a compound expr contains the value.  */
+         tree op1 = TREE_OPERAND (expr,1);
+         tree new_op1 = convert_to_void (op1, implicit);
+         
+         if (new_op1 != op1)
+           expr = build (COMPOUND_EXPR, TREE_TYPE (new_op1),
+                         TREE_OPERAND (expr, 0), new_op1);
+         break;
+       }
+     
+     case NON_LVALUE_EXPR:
+     case NOP_EXPR:
+       /* These have already decayed to rvalue. */
+       break;
+     
+     case CALL_EXPR:   /* we have a special meaning for volatile void fn() */
+       break;
+     
+     case INDIRECT_REF:
+       {
+         tree type = TREE_TYPE (expr);
+         int is_reference = TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0)))
+                            == REFERENCE_TYPE;
+         int is_volatile = TYPE_VOLATILE (type);
+         int is_complete = TYPE_SIZE (complete_type (type)) != NULL_TREE;
+         
+         if (is_volatile && !is_complete)
+           cp_warning ("object of incomplete type `%T' will not be accessed in %s",
+                       type, implicit ? implicit : "void context");
+         else if (is_reference && is_volatile)
+           cp_warning ("object of type `%T' will not be accessed in %s",
+                       TREE_TYPE (TREE_OPERAND (expr, 0)),
+                       implicit ? implicit : "void context");
+         if (is_reference || !is_volatile || !is_complete)
+           expr = TREE_OPERAND (expr, 0);
+       
+         break;
+       }
+     
+     case VAR_DECL:
+       {
+         /* External variables might be incomplete.  */
+         tree type = TREE_TYPE (expr);
+         int is_complete = TYPE_SIZE (complete_type (type)) != NULL_TREE;
+         
+         if (TYPE_VOLATILE (type) && !is_complete)
+           cp_warning ("object `%E' of incomplete type `%T' will not be accessed in %s",
+                       expr, type, implicit ? implicit : "void context");
+         break;
+       }
+     
+     case ADDR_EXPR:
+       op = TREE_OPERAND (expr, 0);
+       if (TREE_CODE (op) != FUNCTION_DECL && TREE_CODE (op) != OVERLOAD)
+         break;
+       /* FALLTHROUGH */
+     
+     case FUNCTION_DECL:
+     case OVERLOAD:
+       if (TREE_CODE (op) == OVERLOAD)
+         {
+           /* [over.over] enumerates the places where we can take the address
+              of an overloaded function, and this is not one of them.  */
+           cp_pedwarn ("%s has no context for overloaded function name `%E'",
+                       implicit ? implicit : "void cast", expr);
+         }
+       else if (implicit)
+         cp_warning ("%s is a reference, not call, to function `%E'",
+                     implicit, expr);
+       break;
+     
+     default:;
+     }
+   
+   if (expr != error_mark_node
+       && !same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (expr)), void_type_node))
+     {
+       /* FIXME: This is where we should check for expressions with no
+          effects.  To do that at the moment requires fixing comma
+          expressions.  Doing that means we don't have to conditionally
+          build the CONVERT_EXPR.  */
+       if (!implicit)
+         expr = build1 (CONVERT_EXPR, void_type_node, expr);
+     }
+   return expr;
  }
  
  /* Create an expression whose value is that of EXPR,
Index: gcc/cp/decl.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/decl.c,v
retrieving revision 1.429
diff -c -3 -p -r1.429 decl.c
*** decl.c	1999/09/06 02:43:06	1.429
--- decl.c	1999/09/06 12:35:01
*************** void
*** 14429,14452 ****
  cplus_expand_expr_stmt (exp)
       tree exp;
  {
!   exp = require_complete_type_in_void (exp);
    
-   if (TREE_CODE (exp) == FUNCTION_DECL)
-     {
-       cp_warning ("reference, not call, to function `%D'", exp);
-       warning ("at this point in file");
-     }
- 
  #if 0
    /* We should do this eventually, but right now this causes regex.o from
       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
--- 14429,14442 ----
  cplus_expand_expr_stmt (exp)
       tree exp;
  {
!   if (stmts_are_full_exprs_p)
!     exp = convert_to_void (exp, "statement");
    
  #if 0
    /* We should do this eventually, but right now this causes regex.o from
       libg++ to miscompile, and tString to core dump.  */
    exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
  #endif
  
    /* If we don't do this, we end up down inside expand_expr
       trying to do TYPE_MODE on the ERROR_MARK, and really
Index: gcc/cp/semantics.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/semantics.c,v
retrieving revision 1.70
diff -c -3 -p -r1.70 semantics.c
*** semantics.c	1999/09/02 19:14:06	1.70
--- semantics.c	1999/09/06 12:35:02
*************** finish_expr_stmt (expr)
*** 79,87 ****
  	  emit_line_note (input_filename, lineno);
  	  /* Do default conversion if safe and possibly important,
  	     in case within ({...}).  */
! 	  if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
! 	       && lvalue_p (expr))
! 	      || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
  	    expr = default_conversion (expr);
  
  	  if (stmts_are_full_exprs_p)
--- 79,88 ----
  	  emit_line_note (input_filename, lineno);
  	  /* Do default conversion if safe and possibly important,
  	     in case within ({...}).  */
! 	  if (!stmts_are_full_exprs_p &&
! 	      ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
! 	        && lvalue_p (expr))
! 	       || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE))
  	    expr = default_conversion (expr);
  
  	  if (stmts_are_full_exprs_p)
Index: gcc/cp/typeck.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/typeck.c,v
retrieving revision 1.202
diff -c -3 -p -r1.202 typeck.c
*** typeck.c	1999/09/04 02:19:29	1.202
--- typeck.c	1999/09/06 12:35:09
*************** require_complete_type (value)
*** 128,227 ****
      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))
-     {
-     case COND_EXPR:
-       {
-         tree op;
-         
-         op = TREE_OPERAND (expr,2);
-         op = require_complete_type_in_void (op);
-         TREE_OPERAND (expr,2) = op;
-         if (op == error_mark_node)
-           {
-             expr = op;
-             break;
-           }
-         
-         /* fallthrough */
-       }
-     
-     case COMPOUND_EXPR:
-       {
-         tree op;
-         
-         op = TREE_OPERAND (expr,1);
-         op = require_complete_type_in_void (op);
-         TREE_OPERAND (expr,1) = op;
-         if (op == error_mark_node)
-           {
-             expr = op;
-             break;
-           }
-         
-         break;
-       }
-     
-     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;
- }
- 
  /* Try to complete TYPE, if it is incomplete.  For example, if TYPE is
     a template instantiation, do the instantiation.  Returns TYPE,
     whether or not it could be completed, unless something goes
--- 128,133 ----
*************** build_x_compound_expr (list)
*** 5187,5192 ****
--- 5093,5099 ----
  
    if (! TREE_SIDE_EFFECTS (TREE_VALUE (list)))
      {
+       /* FIXME: This test should be in the implicit cast to void of the LHS. */
        /* the left-hand operand of a comma expression is like an expression
           statement: we should warn if it doesn't have any side-effects,
           unless it was explicitly cast to (void).  */
*************** build_compound_expr (list)
*** 5235,5241 ****
      }
  
    first = TREE_VALUE (list);
!   first = require_complete_type_in_void (first);
    if (first == error_mark_node)
      return error_mark_node;
    
--- 5142,5148 ----
      }
  
    first = TREE_VALUE (list);
!   first = convert_to_void (first, "lhs of comma");
    if (first == error_mark_node)
      return error_mark_node;
    
*************** build_static_cast (type, expr)
*** 5279,5285 ****
      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
--- 5186,5195 ----
      expr = TREE_OPERAND (expr, 0);
  
    if (TREE_CODE (type) == VOID_TYPE)
!     {
!       expr = convert_to_void (expr, /*implicit=*/NULL);
!       return expr;
!     }
  
    if (TREE_CODE (type) == REFERENCE_TYPE)
      return (convert_from_reference
*************** build_c_cast (type, expr)
*** 5569,5574 ****
--- 5479,5492 ----
        return t;
      }
  
+   if (TREE_CODE (type) == VOID_TYPE)
+     {
+       /* Conversion to void does not cause any of the normal function to
+        * pointer, array to pointer and lvalue to rvalue decays.  */
+       
+       value = convert_to_void (value, /*implicit=*/NULL);
+       return value;
+     }
    /* Convert functions and arrays to pointers and
       convert references to their expanded types,
       but don't convert any other types.  If, however, we are
*************** build_c_cast (type, expr)
*** 5637,5649 ****
      warning ("cast to pointer from integer of different size");
  #endif
  
!   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);
!     }
!   else if (TREE_CODE (type) == REFERENCE_TYPE)
      value = (convert_from_reference
  	     (convert_to_reference (type, value, CONV_C_CAST,
  				    LOOKUP_COMPLAIN, NULL_TREE)));
--- 5555,5561 ----
      warning ("cast to pointer from integer of different size");
  #endif
  
!   if (TREE_CODE (type) == REFERENCE_TYPE)
      value = (convert_from_reference
  	     (convert_to_reference (type, value, CONV_C_CAST,
  				    LOOKUP_COMPLAIN, NULL_TREE)));
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/09/06 12:35:10
***************
*** 10,51 ****
  // because the abstract semantics seem to require the evaluation of such
  // values whether they are volatile or not.
  
  // keywords: incomplete types, evaluation, volatile qualifier
  // Build don't link: 
  
! int i;
  
! 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; }
--- 10,164 ----
  // 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. We interpret a
+ // dereference of pointer to volatile to be a read.
+ 
  // keywords: incomplete types, evaluation, volatile qualifier
  // Build don't link: 
+ 
+ int *ip_fn ();
+ int &ir_fn ();
+ volatile int *vip_fn ();
+ volatile int &vir_fn ();
+ 
+ void int_test (int i, int *p, volatile int *vp, int &r, volatile int &vr)
+ {
+   int j;
+   volatile int vj;
+   
+   *p;				// ok, no warning
+   (void)*p;			// ok, no warning
+   (void)(i ? j : *p);	        // ok, no warning
+   (void)(i ? *p : j);	        // ok, no warning
+   (void)((void)1, *p);	        // ok, no warning
+ 
+   *vp;				// ok, no warning
+   (void)*vp;			// ok, no warning
+   (void)(i ? vj : *vp);	        // ok, no warning
+   (void)(i ? *vp : vj);	        // ok, no warning
+   (void)((void)1, *vp);         // ok, no warning
+ 
+   r;				// ok, no warning
+   (void)r;			// ok, no warning
+   (void)(i ? j : r);	        // ok, no warning
+   (void)(i ? r : j);	        // ok, no warning
+   (void)((void)1, r);	        // ok, no warning
+ 
+   vr;				// WARNING - reference not accessed
+   (void)vr;			// WARNING - reference not accessed
+   (void)(i ? vj : vr);	        // WARNING - reference not accessed
+   (void)(i ? vr : vj);	        // WARNING - reference not accessed
+   (void)((void)1, vr);          // WARNING - reference not accessed
+   
+   *ip_fn ();			// ok, no warning
+   *vip_fn ();			// ok, no warning
+   ir_fn ();			// ok, no warning
+   vir_fn ();			// WARNING - reference not accessed
+ }
  
! struct S;
! S *sp_fn ();
! S &sr_fn ();
! volatile S *vsp_fn ();
! volatile S &vsr_fn ();
  
! void incomplete_test (int i, S *p, volatile S *vp, S &r, volatile S &vr)
! {
!   extern S j;
!   extern volatile S vj;
!   
!   *p;				// ok, no warning
!   (void)*p;			// ok, no warning
!   (void)(i ? j : *p);	        // ok, no warning
!   (void)(i ? *p : j);	        // ok, no warning
!   (void)((void)1, *p);	        // ok, no warning
! 
!   *vp;				// WARNING - incomplete not accessed
!   (void)*vp;			// WARNING - incomplete not accessed
!   (void)(i ? vj : *vp);	        // WARNING - incomplete not accessed
!   (void)(i ? *vp : vj);	        // WARNING - incomplete not accessed
!   (void)((void)1, *vp);         // WARNING - incomplete not accessed
! 
!   r;				// ok, no warning
!   (void)r;			// ok, no warning
!   (void)(i ? j : r);	        // ok, no warning
!   (void)(i ? r : j);	        // ok, no warning
!   (void)((void)1, r);	        // ok, no warning
! 
!   vr;				// WARNING - reference not accessed
!   (void)vr;			// WARNING - reference not accessed
!   (void)(i ? vj : vr);	        // WARNING - reference not accessed
!   (void)(i ? vr : vj);	        // WARNING - reference not accessed
!   (void)((void)1, vr);          // WARNING - reference not accessed
!   
!   *sp_fn ();			// ok, no warning
!   *vsp_fn ();			// WARNING - incomplete not accessed
!   sr_fn ();			// ok, no warning
!   vsr_fn ();			// WARNING - reference not accessed
! }
! 
! struct T {int m;};
! T *tp_fn ();
! T &tr_fn ();
! volatile T *vtp_fn ();
! volatile T &vtr_fn ();
  
! void complete_test (int i, T *p, volatile T *vp, T &r, volatile T &vr)
  {
!   T j;
!   volatile T vj;
!   
!   *p;				// ok, no warning
!   (void)*p;			// ok, no warning
!   (void)(i ? j : *p);	        // ok, no warning
!   (void)(i ? *p : j);	        // ok, no warning
!   (void)((void)1, *p);	        // ok, no warning
! 
!   *vp;				// ok, no warning
!   (void)*vp;			// ok, no warning
!   (void)(i ? vj : *vp);	        // ok, no warning
!   (void)(i ? *vp : vj);	        // ok, no warning
!   (void)((void)1, *vp);         // ok, no warning
! 
!   r;				// ok, no warning
!   (void)r;			// ok, no warning
!   (void)(i ? j : r);	        // ok, no warning
!   (void)(i ? r : j);	        // ok, no warning
!   (void)((void)1, r);	        // ok, no warning
! 
!   vr;				// WARNING - reference not accessed
!   (void)vr;			// WARNING - reference not accessed
!   (void)(i ? vj : vr);	        // WARNING - reference not accessed
!   (void)(i ? vr : vj);	        // WARNING - reference not accessed
!   (void)((void)1, vr);          // WARNING - reference not accessed
!   
!   *tp_fn ();			// ok, no warning
!   *vtp_fn ();			// ok, no warning
!   tr_fn ();			// ok, no warning
!   vtr_fn ();			// ok, no warningWARNING - reference not accessed
  }
  
! void extern_test ()
! {
!   extern S es;
!   extern volatile S ves;
!   extern T et;
!   extern volatile T vet;
!   
!   extern S &esr;
!   extern volatile S &vesr;
!   extern T &etr;
!   extern volatile T &vetr;
!   
!   es;				// ok, no warning
!   ves;				// WARNING - incomplete not accessed
!   et;				// ok, no warning
!   vet;				// ok, no warning
!   
!   esr;				// ok, no warning
!   vesr;				// WARNING - incomplete not accessed
!   etr;				// ok, no warning
!   vetr;				// WARNING - reference not accessed
! }
Index: gcc/testsuite/g++.old-deja/g++.jason/rfg4.C
===================================================================
RCS file: /cvs/egcs/egcs/gcc/testsuite/g++.old-deja/g++.jason/rfg4.C,v
retrieving revision 1.2
diff -c -3 -p -r1.2 rfg4.C
*** rfg4.C	1998/12/16 21:36:22	1.2
--- rfg4.C	1999/09/06 12:35:11
*************** void f2(double) { }
*** 8,12 ****
  void
  test ()
  {
!     i ? f1 : f2;		// gets bogus error - improper overloading
  }
--- 8,13 ----
  void
  test ()
  {
!   void (*ptr) (double);
!   ptr = i ? f1 : f2;		// gets bogus error - improper overloading
  }
Index: gcc/testsuite/g++.old-deja/g++.jason/rfg5.C
===================================================================
RCS file: /cvs/egcs/egcs/gcc/testsuite/g++.old-deja/g++.jason/rfg5.C,v
retrieving revision 1.2
diff -c -3 -p -r1.2 rfg5.C
*** rfg5.C	1998/12/16 21:36:23	1.2
--- rfg5.C	1999/09/06 12:35:11
*************** int *func () { return 0; }
*** 6,10 ****
  void
  test ()
  {
!   *func;			// gets bogus error - improper overloading
  }
--- 6,10 ----
  void
  test ()
  {
!   int *(*p)() = *func;			// gets bogus error - improper overloading
  }
Index: gcc/testsuite/g++.old-deja/g++.pt/explicit1.C
===================================================================
RCS file: /cvs/egcs/egcs/gcc/testsuite/g++.old-deja/g++.pt/explicit1.C,v
retrieving revision 1.3
diff -c -3 -p -r1.3 explicit1.C
*** explicit1.C	1999/02/21 16:38:00	1.3
--- explicit1.C	1999/09/06 12:35:13
*************** void foo(T t) {}
*** 5,9 ****
  
  void bar()
  {
!   (void (*)(double)) &foo<double>;
  }
--- 5,9 ----
  
  void bar()
  {
!   (void) (void (*)(double)) &foo<double>;
  }
Index: gcc/testsuite/g++.old-deja/g++.pt/explicit26.C
===================================================================
RCS file: /cvs/egcs/egcs/gcc/testsuite/g++.old-deja/g++.pt/explicit26.C,v
retrieving revision 1.3
diff -c -3 -p -r1.3 explicit26.C
*** explicit26.C	1999/02/21 16:38:02	1.3
--- explicit26.C	1999/09/06 12:35:13
*************** int foo(int i) { return 0; }
*** 8,12 ****
  
  int main()
  {
!   (int (*)(int)) &foo<int>;
  }
--- 8,12 ----
  
  int main()
  {
!   (void) (int (*)(int)) &foo<int>;
  }
Index: gcc/testsuite/g++.old-deja/g++.pt/explicit27.C
===================================================================
RCS file: /cvs/egcs/egcs/gcc/testsuite/g++.old-deja/g++.pt/explicit27.C,v
retrieving revision 1.3
diff -c -3 -p -r1.3 explicit27.C
*** explicit27.C	1999/02/21 16:38:03	1.3
--- explicit27.C	1999/09/06 12:35:13
*************** void foo(int i) {}
*** 8,12 ****
  
  int main()
  {
!   (void (*)(int)) &foo<int>;
  }
--- 8,12 ----
  
  int main()
  {
!   (void) (void (*)(int)) &foo<int>;
  }
Index: gcc/testsuite/g++.old-deja/g++.pt/explicit3.C
===================================================================
RCS file: /cvs/egcs/egcs/gcc/testsuite/g++.old-deja/g++.pt/explicit3.C,v
retrieving revision 1.2
diff -c -3 -p -r1.2 explicit3.C
*** explicit3.C	1998/12/16 21:55:22	1.2
--- explicit3.C	1999/09/06 12:35:13
*************** void foo(T t, U u) {}
*** 5,9 ****
  
  void bar()
  {
!   (void (*)(double, int)) &foo<double>;
  }
--- 5,9 ----
  
  void bar()
  {
!   (void) (void (*)(double, int)) &foo<double>;
  }


More information about the Gcc-patches mailing list