From 02cac427d515af0b0855cda11124997fc76a13b9 Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Thu, 9 Sep 1999 12:07:46 +0000 Subject: [PATCH] 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 --- gcc/ChangeLog | 4 ++ gcc/cp/ChangeLog | 16 ++++++ gcc/cp/cp-tree.h | 2 +- gcc/cp/cvt.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++-- gcc/cp/decl.c | 14 +----- gcc/cp/semantics.c | 7 +-- gcc/cp/typeck.c | 118 ++++++--------------------------------------- gcc/extend.texi | 82 +++++++++++++++++++++++++++++++ 8 files changed, 259 insertions(+), 123 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1f11bc7..56c5145 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +Thu Sep 9 12:32:57 BST 1999 Nathan Sidwell + + * extend.texi (Volatiles): New node. + Thu Sep 9 12:20:34 1999 Nick Clifton * toplev.c (documented_lang_options): diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 514cd2f..5819eca 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,19 @@ +1999-09-09 Nathan Sidwell + + * 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 * cp-tree.h (lang_decl_flags): Remove permanent_attr. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4a37cdb..fa6a7fa 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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)); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 73e76dd..7ea55bd 100644 --- a/gcc/cp/cvt.c +++ b/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 diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 6305a62..3286709 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index bd44449..822027e 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index b035fbe..506103b 100644 --- a/gcc/cp/typeck.c +++ b/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))); diff --git a/gcc/extend.texi b/gcc/extend.texi index 8a3a7e7..5b5f3b1 100644 --- a/gcc/extend.texi +++ b/gcc/extend.texi @@ -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{?} are built into the compiler, they properly handle expressions with side-effects; @w{@samp{int min = i++ ; +volatile int *src = ; +*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 = ; +*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 = ; +volatile T *ptr2 = ; +*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(*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 -- 2.7.4