extend.texi (Volatiles): New node.

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
This commit is contained in:
Nathan Sidwell 1999-09-09 12:07:46 +00:00 committed by Nathan Sidwell
parent fb3f91c9a8
commit 02cac427d5
8 changed files with 259 additions and 123 deletions

View 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):

View 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.

View 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));

View 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

View 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. */

View 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)

View 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)));

View 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