mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-23 03:19:12 +08:00
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:
parent
fb3f91c9a8
commit
02cac427d5
@ -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):
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
139
gcc/cp/cvt.c
139
gcc/cp/cvt.c
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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)
|
||||
|
118
gcc/cp/typeck.c
118
gcc/cp/typeck.c
@ -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)));
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user