extend.texi (Volatiles): New node.
authorNathan Sidwell <nathan@acm.org>
Thu, 9 Sep 1999 12:07:46 +0000 (12:07 +0000)
committerNathan Sidwell <nathan@gcc.gnu.org>
Thu, 9 Sep 1999 12:07:46 +0000 (12:07 +0000)
gcc/ChangeLog:
* extend.texi (Volatiles): New node.
gcc/cp/ChangeLog:
* 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.

From-SVN: r29233

gcc/ChangeLog
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/cvt.c
gcc/cp/decl.c
gcc/cp/semantics.c
gcc/cp/typeck.c
gcc/extend.texi

index 1f11bc7..56c5145 100644 (file)
@@ -1,3 +1,7 @@
+Thu Sep  9 12:32:57 BST 1999  Nathan Sidwell  <nathan@acm.org>
+
+       * extend.texi (Volatiles): New node.
+
 Thu Sep  9 12:20:34 1999  Nick Clifton  <nickc@cygnus.com>
 
        * toplev.c (documented_lang_options): 
index 514cd2f..5819eca 100644 (file)
@@ -1,3 +1,19 @@
+1999-09-09  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.
+
 1999-09-08  Mark Mitchell  <mark@codesourcery.com>
 
        * cp-tree.h (lang_decl_flags): Remove permanent_attr.
index 4a37cdb..fa6a7fa 100644 (file)
@@ -3044,6 +3044,7 @@ extern tree convert_pointer_to_real               PROTO((tree, tree));
 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));
@@ -3718,7 +3719,6 @@ extern int string_conv_p                  PROTO((tree, tree, int));
 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));
index 73e76dd..7ea55bd 100644 (file)
@@ -715,10 +715,7 @@ ocp_convert (type, expr, convtype, flags)
 
   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);
-
+      e = convert_to_void (e, /*implicit=*/NULL);
       return e;
     }
 
@@ -856,6 +853,140 @@ ocp_convert (type, expr, convtype, flags)
   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;
+{
+  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;
+      }
+    
+    default:;
+    }
+  {
+    tree probe = expr;
+  
+    if (TREE_CODE (probe) == ADDR_EXPR)
+      probe = TREE_OPERAND (expr, 0);
+    if (!is_overloaded_fn (probe))
+      ;/* OK */
+    else if (really_overloaded_fn (probe))
+        {
+          /* [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 && probe == expr)
+      /* Only warn when there is no &.  */
+      cp_warning ("%s is a reference, not call, to function `%E'",
+                    implicit, expr);
+  }
+  
+  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.  At the moment we do that in both build_x_component_expr
+         and expand_expr_stmt -- inconsistently too.  For the moment
+         leave implicit void conversions unadorned so that expand_expr_stmt
+         has a chance of detecting some of the cases.  */
+      if (!implicit)
+        expr = build1 (CONVERT_EXPR, void_type_node, expr);
+    }
+  return expr;
+}
+
 /* Create an expression whose value is that of EXPR,
    converted to type TYPE.  The TREE_TYPE of the value
    is always TYPE.  This function implements all reasonable
index 6305a62..3286709 100644 (file)
@@ -14336,25 +14336,15 @@ void
 cplus_expand_expr_stmt (exp)
      tree exp;
 {
-  exp = require_complete_type_in_void (exp);
+  if (stmts_are_full_exprs_p)
+    exp = convert_to_void (exp, "statement");
   
-  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
      go outside the bounds of the type.  */
index bd44449..822027e 100644 (file)
@@ -79,9 +79,10 @@ finish_expr_stmt (expr)
          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)
+         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 b035fbe..506103b 100644 (file)
@@ -128,100 +128,6 @@ require_complete_type (value)
     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
@@ -5160,6 +5066,7 @@ build_x_compound_expr (list)
 
   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).  */
@@ -5208,7 +5115,7 @@ build_compound_expr (list)
     }
 
   first = TREE_VALUE (list);
-  first = require_complete_type_in_void (first);
+  first = convert_to_void (first, "lhs of comma");
   if (first == error_mark_node)
     return error_mark_node;
   
@@ -5251,7 +5158,10 @@ build_static_cast (type, expr)
     expr = TREE_OPERAND (expr, 0);
 
   if (TREE_CODE (type) == VOID_TYPE)
-    return build1 (CONVERT_EXPR, type, expr);
+    {
+      expr = convert_to_void (expr, /*implicit=*/NULL);
+      return expr;
+    }
 
   if (TREE_CODE (type) == REFERENCE_TYPE)
     return (convert_from_reference
@@ -5539,6 +5449,14 @@ build_c_cast (type, expr)
       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
@@ -5607,13 +5525,7 @@ build_c_cast (type, expr)
     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)
+  if (TREE_CODE (type) == REFERENCE_TYPE)
     value = (convert_from_reference
             (convert_to_reference (type, value, CONV_C_CAST,
                                    LOOKUP_COMPLAIN, NULL_TREE)));
index 8a3a7e7..5b5f3b1 100644 (file)
@@ -3157,6 +3157,7 @@ Predefined Macros,cpp.info,The C Preprocessor}).
 @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
@@ -3323,6 +3324,87 @@ 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