From 7e99327dbcd3f1d453088f63eebe65994aded8c2 Mon Sep 17 00:00:00 2001 From: Mark Mitchell Date: Mon, 1 Sep 2003 19:18:03 +0000 Subject: [PATCH] re PR c++/12114 ([3.3.2] Uninitialized memory accessed in dtor) PR c++/12114 * g++.dg/init/ref9.C: New test. PR c++/11972 * g++.dg/template/nested4.C: New test. PR c++/12114 * cp-tree.h (initialize_reference): Change prototype. * call.c (initialize_reference): Add cleanup parameter. * decl.c (grok_reference_init): Likewise. (check_initializer): Likewise. (cp_finish_decl): Insert a CLEANUP_STMT if necessary. (duplicate_decls): When replacing an anticipated builtin, do not honor TREE_NOTHROW. * typeck.c (convert_for_initialization): Correct call to initialize_reference. PR c++/11972 * pt.c (dependent_type_p_r): Pass only the innermost template arguments to any_dependent_template_arguments_p. From-SVN: r70981 --- gcc/cp/ChangeLog | 17 +++++++++ gcc/cp/call.c | 40 ++++++++++++++++----- gcc/cp/cp-tree.h | 2 +- gcc/cp/decl.c | 48 +++++++++++++++++-------- gcc/cp/pt.c | 3 +- gcc/cp/typeck.c | 3 +- gcc/testsuite/ChangeLog | 8 +++++ gcc/testsuite/g++.dg/init/ref9.C | 36 +++++++++++++++++++ gcc/testsuite/g++.dg/template/nested4.C | 10 ++++++ 9 files changed, 140 insertions(+), 27 deletions(-) create mode 100644 gcc/testsuite/g++.dg/init/ref9.C create mode 100644 gcc/testsuite/g++.dg/template/nested4.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 55635df2024..326da4ba433 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,20 @@ +2003-09-01 Mark Mitchell + + PR c++/12114 + * cp-tree.h (initialize_reference): Change prototype. + * call.c (initialize_reference): Add cleanup parameter. + * decl.c (grok_reference_init): Likewise. + (check_initializer): Likewise. + (cp_finish_decl): Insert a CLEANUP_STMT if necessary. + (duplicate_decls): When replacing an anticipated builtin, do not + honor TREE_NOTHROW. + * typeck.c (convert_for_initialization): Correct call to + initialize_reference. + + PR c++/11972 + * pt.c (dependent_type_p_r): Pass only the innermost template + arguments to any_dependent_template_arguments_p. + 2003-09-01 Josef Zlomek * error.c (dump_expr): Kill BIT_ANDTC_EXPR. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index a74fd63d556..9e46282abd0 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -6005,14 +6005,18 @@ make_temporary_var_for_ref_to_temp (tree decl, tree type) } /* Convert EXPR to the indicated reference TYPE, in a way suitable for - initializing a variable of that TYPE. If DECL is non-NULL, it is + initializing a variable of that TYPE. If DECL is non-NULL, it is the VAR_DECL being initialized with the EXPR. (In that case, the - type of DECL will be TYPE.) + type of DECL will be TYPE.) If DECL is non-NULL, then CLEANUP must + also be non-NULL, and with *CLEANUP initialized to NULL. Upon + return, if *CLEANUP is no longer NULL, it will be a CLEANUP_STMT + that should be inserted after the returned expression is used to + initialize DECL. Return the converted expression. */ tree -initialize_reference (tree type, tree expr, tree decl) +initialize_reference (tree type, tree expr, tree decl, tree *cleanup) { tree conv; @@ -6094,14 +6098,33 @@ initialize_reference (tree type, tree expr, tree decl) type = TREE_TYPE (expr); var = make_temporary_var_for_ref_to_temp (decl, type); layout_decl (var, 0); + /* Create the INIT_EXPR that will initialize the temporary + variable. */ + init = build (INIT_EXPR, type, var, expr); if (at_function_scope_p ()) { - tree cleanup; - add_decl_stmt (var); - cleanup = cxx_maybe_build_cleanup (var); - if (cleanup) - finish_decl_cleanup (var, cleanup); + *cleanup = cxx_maybe_build_cleanup (var); + if (*cleanup) + /* We must be careful to destroy the temporary only + after its initialization has taken place. If the + initialization throws an exception, then the + destructor should not be run. We cannot simply + transform INIT into something like: + + (INIT, ({ CLEANUP_STMT; })) + + because emit_local_var always treats the + initializer as a full-expression. Thus, the + destructor would run too early; it would run at the + end of initializing the reference variable, rather + than at the end of the block enclosing the + reference variable. + + The solution is to pass back a CLEANUP_STMT which + the caller is responsible for attaching to the + statement tree. */ + *cleanup = build_stmt (CLEANUP_STMT, var, *cleanup); } else { @@ -6110,7 +6133,6 @@ initialize_reference (tree type, tree expr, tree decl) static_aggregates = tree_cons (NULL_TREE, var, static_aggregates); } - init = build (INIT_EXPR, type, var, expr); /* Use its address to initialize the reference variable. */ expr = build_address (var); expr = build (COMPOUND_EXPR, TREE_TYPE (expr), init, expr); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 1a2f06e1ad2..f17b67c2d00 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3524,7 +3524,7 @@ extern tree type_passed_as (tree); extern tree convert_for_arg_passing (tree, tree); extern tree cp_convert_parm_for_inlining (tree, tree, tree); extern bool is_properly_derived_from (tree, tree); -extern tree initialize_reference (tree, tree, tree); +extern tree initialize_reference (tree, tree, tree, tree *); extern tree make_temporary_var_for_ref_to_temp (tree, tree); extern tree strip_top_quals (tree); extern tree perform_implicit_conversion (tree, tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index b4eb0192eb8..4c287ea7158 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -68,7 +68,7 @@ static cxx_saved_binding *store_bindings (tree, cxx_saved_binding *); static tree lookup_tag_reverse (tree, tree); static void push_local_name (tree); static void warn_extern_redeclared_static (tree, tree); -static tree grok_reference_init (tree, tree, tree); +static tree grok_reference_init (tree, tree, tree, tree *); static tree grokfndecl (tree, tree, tree, tree, int, enum overload_flags, tree, tree, int, int, int, int, int, int, tree); @@ -117,7 +117,7 @@ static void pop_labels (tree); static void maybe_deduce_size_from_array_init (tree, tree); static void layout_var_decl (tree); static void maybe_commonize_var (tree); -static tree check_initializer (tree, tree, int); +static tree check_initializer (tree, tree, int, tree *); static void make_rtl_for_nonlocal_decl (tree, tree, const char *); static void save_function_data (tree); static void check_function_type (tree, tree); @@ -2940,9 +2940,9 @@ duplicate_decls (tree newdecl, tree olddecl) if (DECL_ANTICIPATED (olddecl)) ; /* Do nothing yet. */ else if ((DECL_EXTERN_C_P (newdecl) - && DECL_EXTERN_C_P (olddecl)) - || compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)), - TYPE_ARG_TYPES (TREE_TYPE (olddecl)))) + && DECL_EXTERN_C_P (olddecl)) + || compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)), + TYPE_ARG_TYPES (TREE_TYPE (olddecl)))) { /* A near match; override the builtin. */ @@ -2969,6 +2969,10 @@ duplicate_decls (tree newdecl, tree olddecl) else if (DECL_ANTICIPATED (olddecl)) TREE_TYPE (olddecl) = TREE_TYPE (newdecl); + /* Whether or not the builtin can throw exceptions has no + bearing on this declarator. */ + TREE_NOTHROW (olddecl) = 0; + if (DECL_THIS_STATIC (newdecl) && !DECL_THIS_STATIC (olddecl)) { /* If a builtin function is redeclared as `static', merge @@ -7174,14 +7178,18 @@ start_decl_1 (tree decl) DECL_INITIAL (decl) = NULL_TREE; } -/* Handle initialization of references. - These three arguments are from `cp_finish_decl', and have the - same meaning here that they do there. +/* Handle initialization of references. DECL, TYPE, and INIT have the + same meaning as in cp_finish_decl. *CLEANUP must be NULL on entry, + but will be set to a new CLEANUP_STMT if a temporary is created + that must be destroeyd subsequently. + + Returns an initializer expression to use to initialize DECL, or + NULL if the initialization can be performed statically. Quotes on semantics can be found in ARM 8.4.3. */ static tree -grok_reference_init (tree decl, tree type, tree init) +grok_reference_init (tree decl, tree type, tree init, tree *cleanup) { tree tmp; @@ -7218,7 +7226,7 @@ grok_reference_init (tree decl, tree type, tree init) DECL_INITIAL for local references (instead assigning to them explicitly); we need to allow the temporary to be initialized first. */ - tmp = initialize_reference (type, init, decl); + tmp = initialize_reference (type, init, decl, cleanup); if (tmp == error_mark_node) return NULL_TREE; @@ -7638,13 +7646,14 @@ reshape_init (tree type, tree *initp) } /* Verify INIT (the initializer for DECL), and record the - initialization in DECL_INITIAL, if appropriate. + initialization in DECL_INITIAL, if appropriate. CLEANUP is as for + grok_reference_init. If the return value is non-NULL, it is an expression that must be evaluated dynamically to initialize DECL. */ static tree -check_initializer (tree decl, tree init, int flags) +check_initializer (tree decl, tree init, int flags, tree *cleanup) { tree type = TREE_TYPE (decl); @@ -7694,7 +7703,7 @@ check_initializer (tree decl, tree init, int flags) init = NULL_TREE; } else if (!DECL_EXTERNAL (decl) && TREE_CODE (type) == REFERENCE_TYPE) - init = grok_reference_init (decl, type, init); + init = grok_reference_init (decl, type, init, cleanup); else if (init) { if (TREE_CODE (init) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (init)) @@ -8001,8 +8010,9 @@ initialize_local_var (tree decl, tree init) void cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags) { - register tree type; + tree type; tree ttype = NULL_TREE; + tree cleanup; const char *asmspec = NULL; int was_readonly = 0; @@ -8015,6 +8025,9 @@ cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags) my_friendly_assert (TREE_CODE (decl) != RESULT_DECL, 20030619); + /* Assume no cleanup is required. */ + cleanup = NULL_TREE; + /* If a name was specified, get the string. */ if (global_scope_p (current_binding_level)) asmspec_tree = maybe_apply_renaming_pragma (decl, asmspec_tree); @@ -8128,7 +8141,7 @@ cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags) is *not* defined. */ && (!DECL_EXTERNAL (decl) || init)) { - init = check_initializer (decl, init, flags); + init = check_initializer (decl, init, flags, &cleanup); /* Thread-local storage cannot be dynamically initialized. */ if (DECL_THREAD_LOCAL (decl) && init) { @@ -8244,6 +8257,11 @@ cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags) } } + /* If a CLEANUP_STMT was created to destroy a temporary bound to a + reference, insert it in the statement-tree now. */ + if (cleanup) + add_stmt (cleanup); + finish_end: if (was_readonly) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 0c5714363f1..8cf8d15ff84 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -11332,7 +11332,8 @@ dependent_type_p_r (tree type) /* ... or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent. */ else if (CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INFO (type) - && any_dependent_template_arguments_p (CLASSTYPE_TI_ARGS (type))) + && (any_dependent_template_arguments_p + (INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type))))) return true; /* All TYPEOF_TYPEs are dependent; if the argument of the `typeof' diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 91aab2f99e8..564b3373aec 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -5657,7 +5657,8 @@ convert_for_initialization (tree exp, tree type, tree rhs, int flags, if (fndecl) savew = warningcount, savee = errorcount; - rhs = initialize_reference (type, rhs, /*decl=*/NULL_TREE); + rhs = initialize_reference (type, rhs, /*decl=*/NULL_TREE, + /*cleanup=*/NULL); if (fndecl) { if (warningcount > savew) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 8eeca7e50c3..b62f69c9ffc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2003-09-01 Mark Mitchell + + PR c++/12114 + * g++.dg/init/ref9.C: New test. + + PR c++/11972 + * g++.dg/template/nested4.C: New test. + 2003-08-29 Mark Mitchell PR c++/12093 diff --git a/gcc/testsuite/g++.dg/init/ref9.C b/gcc/testsuite/g++.dg/init/ref9.C new file mode 100644 index 00000000000..127b7d8e1fd --- /dev/null +++ b/gcc/testsuite/g++.dg/init/ref9.C @@ -0,0 +1,36 @@ +// { dg-do run } + +struct ex; +struct basic { + int refcount; + ex eval() const; + basic() : refcount(0) {} +}; + +struct ex { + basic *bp; + ex() : bp(0) { } + ex(const basic &); + virtual ~ex(); + void construct_from_basic(const basic &); +}; + +ex basic::eval() const { + throw 1; +} + +inline ex::ex(const basic &b) { construct_from_basic (b); } +inline ex::~ex() { if (--bp->refcount == 0) delete bp; } +void ex::construct_from_basic(const basic &b) { + const ex & tmpex = b.eval(); + bp = tmpex.bp; + bp->refcount++; +} + +ex pow() { return basic(); } + +int main() +{ + try { pow (); } catch (int) {} + return 0; +} diff --git a/gcc/testsuite/g++.dg/template/nested4.C b/gcc/testsuite/g++.dg/template/nested4.C new file mode 100644 index 00000000000..6e5b99b46c2 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/nested4.C @@ -0,0 +1,10 @@ +template struct A { + template struct B { typedef A X; }; + +}; + +template void f() { + typedef A::B::X X; +} + +template void f (); -- 2.30.2