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