This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH: expressions in void context
- To: egcs-patches at egcs dot cygnus dot com
- Subject: C++ PATCH: expressions in void context
- From: Nathan Sidwell <nathan at acm dot org>
- Date: Fri, 27 Aug 1999 10:07:04 +0100
- Reply-To: nathan at compsci dot bristol dot ac dot uk
Hi,
attached is a patch which fixes
http://egcs.cygnus.com/ml/gcc-bugs/1999-06/msg00468.html and implements
the final sentance of 5.2.9/4
(http://egcs.cygnus.com/ml/gcc-bugs/1999-06/msg00493.html gives an
explanation)
The patch renames require_complete_type_in_void to check_expr_in_void.
And this then
makes sure that no implicit dereference happens. If an lvalue object is
accessed, the access is removed (no rvalue decay).
cplus_expand_stmt (decl.c) only calls check_expr_in_void when dealing
with full statements (otherwise the result value of incomplete
statements is removed). It is also unnecessary for unused reference
values to be stripped here, because check_expr_in_void does that.
Should the accessed object have volatile type, and extra_warnings
enabled, a warning is issued about the volatile not being accessed.
The patch amends g++bug/90428_01.C to test for the correct behaviour of
5.2.9/4. Please note that this means two cases in there now start
failing (lines 32 and 36) due to a problem with conditional exprs. A
separate patch for that problem is on its way.
The attached test case makes sure that code generation does not occur,
by doing things like `*(volatile int *)0', which would segfault
otherwise.
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-08-27 Nathan Sidwell <nathan@acm.org>
* cp-tree.h (require_complete_type_in_void): Rename to ...
(check_expr_in_void): ... here.
* typeck.c (require_complete_type_in_void): Rename to ...
(check_expr_in_void): ... here. Don't check incompleteness. Do
check volatileness, prevent access to volatile objects.
(build_compound_expr): Adjust.
(build_static_cast): Call check_expr_in_void.
(build_c_cast): Adjust.
* cvt.c (ocp_convert): Adjust.
* decl.c (cplus_expand_expr_stmt): Only check_expr_in_void when
dealing with complete expressions. No need to strip
INDIRECT_REFs.
Index: gcc/cp/cp-tree.h
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.271
diff -c -3 -p -r1.271 cp-tree.h
*** cp-tree.h 1999/08/25 22:07:01 1.271
--- cp-tree.h 1999/08/27 08:07:26
*************** extern int string_conv_p PROTO((tree,
*** 3504,3510 ****
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));
--- 3504,3510 ----
extern tree condition_conversion PROTO((tree));
extern tree target_type PROTO((tree));
extern tree require_complete_type PROTO((tree));
! extern tree check_expr_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: gcc/cp/typeck.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/typeck.c,v
retrieving revision 1.198
diff -c -3 -p -r1.198 typeck.c
*** typeck.c 1999/08/25 22:07:16 1.198
--- typeck.c 1999/08/27 08:07:44
*************** require_complete_type (value)
*** 129,142 ****
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))
--- 129,147 ----
return error_mark_node;
}
! /* When an expression is used in a void context, it's value is discarded
! and no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
! stmt.expr/1, expr.comma/1]. This means that `*pvi' where pvi is
! `volatile int *' does NOT result in a dereference. We have to remove the
! dereference in these cases, so that code generation does not emit a
! volatile access.
! Similarly there is no requirement that the expression have complete type,
! or reference an object -- both `*(void *)e' and `*(int *)0' are valid.
! Because this makes a difference when the object is volatile, we issue a
! warning in that case. */
tree
! check_expr_in_void (expr)
tree expr;
{
switch (TREE_CODE (expr))
*************** require_complete_type_in_void (expr)
*** 146,152 ****
tree op;
op = TREE_OPERAND (expr,2);
! op = require_complete_type_in_void (op);
TREE_OPERAND (expr,2) = op;
if (op == error_mark_node)
{
--- 151,157 ----
tree op;
op = TREE_OPERAND (expr,2);
! op = check_expr_in_void (op);
TREE_OPERAND (expr,2) = op;
if (op == error_mark_node)
{
*************** require_complete_type_in_void (expr)
*** 162,168 ****
tree op;
op = TREE_OPERAND (expr,1);
! op = require_complete_type_in_void (op);
TREE_OPERAND (expr,1) = op;
if (op == error_mark_node)
{
--- 167,173 ----
tree op;
op = TREE_OPERAND (expr,1);
! op = check_expr_in_void (op);
TREE_OPERAND (expr,1) = op;
if (op == error_mark_node)
{
*************** require_complete_type_in_void (expr)
*** 175,223 ****
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;
--- 180,208 ----
case NON_LVALUE_EXPR:
case NOP_EXPR:
! /* These have already decayed to rvalue and checked for completeness. */
! break;
case RTL_EXPR: /* RTL nodes have no value */
case DELETE_EXPR: /* delete expressions have no type */
case VEC_DELETE_EXPR:
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: /* ok */
! case CALL_EXPR: /* we have a special meaning for volatile void fn() */
break;
default:
! if (extra_warnings && TREE_TYPE (expr) && TYPE_VOLATILE (TREE_TYPE (expr)))
! cp_warning ("volatile object in void context will not be accessed");
! /* Make sure the object is not accessed. */
! if (TREE_CODE (expr) == INDIRECT_REF)
! expr = TREE_OPERAND (expr,0);
! else if (TREE_CODE (expr) == VAR_DECL || TREE_CODE (expr) == PARM_DECL)
! expr = void_zero_node;
}
return expr;
*************** build_compound_expr (list)
*** 5229,5235 ****
}
first = TREE_VALUE (list);
! first = require_complete_type_in_void (first);
if (first == error_mark_node)
return error_mark_node;
--- 5234,5240 ----
}
first = TREE_VALUE (list);
! first = check_expr_in_void (first);
if (first == error_mark_node)
return error_mark_node;
*************** build_static_cast (type, expr)
*** 5273,5279 ****
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
--- 5278,5287 ----
expr = TREE_OPERAND (expr, 0);
if (TREE_CODE (type) == VOID_TYPE)
! {
! expr = check_expr_in_void (expr);
! return build1 (CONVERT_EXPR, type, expr);
! }
if (TREE_CODE (type) == REFERENCE_TYPE)
return (convert_from_reference
*************** build_c_cast (type, expr)
*** 5633,5639 ****
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);
}
--- 5641,5647 ----
if (TREE_CODE (type) == VOID_TYPE)
{
! value = check_expr_in_void (value);
if (value != error_mark_node)
value = build1 (CONVERT_EXPR, void_type_node, value);
}
Index: gcc/cp/cvt.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/cvt.c,v
retrieving revision 1.63
diff -c -3 -p -r1.63 cvt.c
*** cvt.c 1999/08/11 20:22:25 1.63
--- cvt.c 1999/08/27 08:07:27
*************** ocp_convert (type, expr, convtype, flags
*** 689,695 ****
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);
--- 689,695 ----
if (code == VOID_TYPE && (convtype & CONV_STATIC))
{
! e = check_expr_in_void (e);
if (e != error_mark_node)
e = build1 (CONVERT_EXPR, void_type_node, e);
Index: gcc/cp/decl.c
===================================================================
RCS file: /cvs/egcs/egcs/gcc/cp/decl.c,v
retrieving revision 1.415
diff -c -3 -p -r1.415 decl.c
*** decl.c 1999/08/26 04:19:51 1.415
--- decl.c 1999/08/27 08:07:37
*************** void
*** 14272,14278 ****
cplus_expand_expr_stmt (exp)
tree exp;
{
! exp = require_complete_type_in_void (exp);
if (TREE_CODE (exp) == FUNCTION_DECL)
{
--- 14272,14279 ----
cplus_expand_expr_stmt (exp)
tree exp;
{
! if (stmts_are_full_exprs_p)
! exp = check_expr_in_void (exp);
if (TREE_CODE (exp) == FUNCTION_DECL)
{
*************** cplus_expand_expr_stmt (exp)
*** 14285,14295 ****
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
--- 14286,14291 ----
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/08/27 08:07:44
***************
*** 1,4 ****
--- 1,5 ----
// g++ 1.37.1 bug 900428_01
+ // Special g++ Options: -W -ansi -pedantic
// g++ fails to issue error messages for cases where an incomplete type
// object must be evaluated if the value of such an evaluation is not
***************
*** 10,15 ****
--- 11,20 ----
// 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.
+
// keywords: incomplete types, evaluation, volatile qualifier
// Build don't link:
*************** int i;
*** 17,51 ****
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; }
--- 22,56 ----
void *pv;
volatile void *pvv;
! struct s;
! extern struct s es, *ps;
! extern volatile struct s evs, *pvs;
void pv_test ()
{
! (void)*pv; // ok, no warning
! (void)(i ? *pv : *pv); // ERROR - dereference void *
! (void)((void)*pv, *pv); // ok, no warning
!
! (void)*pvv; // WARNING - volatile not accessed
! (void)(i ? *pvv : *pvv); // ERROR - dereference void *
! (void)((void)*pvv, *pvv); // WARNING - volatile not accessed
!
! (void)es; // ok
! (void)(i ? es : es); // ok
! (void)((void)es, es); // ok
!
! (void)evs; // WARNING - volatile not accessed
! (void)(i ? evs : evs); // WARNING - volatile not accessed
! (void)((void)evs, evs); // WARNING - volatile not accessed
!
! (void)*ps; // ok
! (void)(i ? *ps : *ps); // ok
! (void)((void)*ps, *ps); // ok
!
! (void)*pvs; // WARNING - volatile not accessed
! (void)(i ? *pvs : *pvs); // WARNING - volatile not accessed
! (void)((void)*pvs, *pvs); // WARNING - volatile not accessed
}
int main () { return 0; }
// Copyright (C) 1999 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 24 Aug 1999 <nathan@acm.org>
// lvalues in void contexts do not decay, so volatiles are not referenced.
// Make sure we DTRT
volatile int &fnr(volatile int *p) {return *p;}
volatile int *fnp(volatile int *p) {return p;}
struct S {int t;};
extern volatile S s; // never defined, so we get a link error if used.
void f (int i, volatile int *ip, volatile int &ir)
{
*(void *)0;
*ip;
*ip, *ip;
i ? *ip : *ip;
ir;
ir, ir;
i ? ir : ir;
fnr(ip);
*fnp(ip);
s;
}
int main()
{
// although passing NULL, we never actually dereference it.
// if we do dereference it, we'll seg fault
f(0, 0, *(int *)0);
return 0;
}