diff options
author | Mark Mitchell <mark@codesourcery.com> | 1999-09-25 18:10:04 +0000 |
---|---|---|
committer | Mark Mitchell <mmitchel@gcc.gnu.org> | 1999-09-25 18:10:04 +0000 |
commit | efee38a99ac1639e29a1f08235756becdbeca01c (patch) | |
tree | 29d37f53a0796acd0b38eaf7a9a35991a1aa4bf6 | |
parent | 5a657fc371aefb7648ad42cc9db9187032a80809 (diff) | |
download | gcc-efee38a99ac1639e29a1f08235756becdbeca01c.zip gcc-efee38a99ac1639e29a1f08235756becdbeca01c.tar.gz gcc-efee38a99ac1639e29a1f08235756becdbeca01c.tar.bz2 |
cp-tree.h (check_return_expr): New function.
* cp-tree.h (check_return_expr): New function.
* decl.c (finish_constructor_body): New function.
(pushdecl): Put global friend functions in namespace binding
level, not the class binding level.
(finish_destructor_body): Make sure the dtor_label is always
defined. Fix typo in comment.
(finish_function): Move generation of constructor-termination code
to semantic-analysis time. Move generation of implicit `main'
return value to semantic-analysis time.
* semantics.c (finish_return_stmt): Generate goto's to
ctor_label/dtor_label here. Use check_return_expr to do semantic
analysis on the returned expression.
* typeck.c (maybe_warn_about_returning_address_of_local): New
function split out from c_expand_return.
(check_return_expr): Likewise.
(c_expand_return): Just generate the RTL for the return.
From-SVN: r29663
-rw-r--r-- | gcc/cp/ChangeLog | 19 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/decl.c | 67 | ||||
-rw-r--r-- | gcc/cp/semantics.c | 27 | ||||
-rw-r--r-- | gcc/cp/typeck.c | 336 |
5 files changed, 266 insertions, 184 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 19485a5..0efaa14 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,22 @@ +1999-09-25 Mark Mitchell <mark@codesourcery.com> + + * cp-tree.h (check_return_expr): New function. + * decl.c (finish_constructor_body): New function. + (pushdecl): Put global friend functions in namespace binding + level, not the class binding level. + (finish_destructor_body): Make sure the dtor_label is always + defined. Fix typo in comment. + (finish_function): Move generation of constructor-termination code + to semantic-analysis time. Move generation of implicit `main' + return value to semantic-analysis time. + * semantics.c (finish_return_stmt): Generate goto's to + ctor_label/dtor_label here. Use check_return_expr to do semantic + analysis on the returned expression. + * typeck.c (maybe_warn_about_returning_address_of_local): New + function split out from c_expand_return. + (check_return_expr): Likewise. + (c_expand_return): Just generate the RTL for the return. + 1999-09-24 Mark Mitchell <mark@codesourcery.com> * cp-tree.h (CPTI_CLEANUP_TYPE): New macro. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 584600e..15c5099 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3949,6 +3949,7 @@ extern tree pfn_from_ptrmemfunc PROTO((tree)); extern tree type_after_usual_arithmetic_conversions PROTO((tree, tree)); extern tree composite_pointer_type PROTO((tree, tree, tree, tree, const char*)); +extern tree check_return_expr PROTO((tree)); /* in typeck2.c */ extern tree error_not_base_type PROTO((tree, tree)); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index f456dbb..d5da38f 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -180,6 +180,7 @@ static void save_function_data PROTO((tree)); static void check_function_type PROTO((tree)); static void destroy_local_static PROTO((tree)); static void destroy_local_var PROTO((tree)); +static void finish_constructor_body PROTO((void)); static void finish_destructor_body PROTO((void)); #if defined (DEBUG_CP_BINDING_LEVELS) @@ -4064,7 +4065,10 @@ pushdecl (x) } if (need_new_binding) - add_decl_to_level (x, current_binding_level); + add_decl_to_level (x, + DECL_NAMESPACE_SCOPE_P (x) + ? NAMESPACE_LEVEL (CP_DECL_CONTEXT (x)) + : current_binding_level); return x; } @@ -13329,9 +13333,26 @@ save_function_data (decl) f->cannot_inline = current_function_cannot_inline; } +/* At the end of every constructor we generate to code to return + `this'. Do that now. */ + +static void +finish_constructor_body () +{ + /* Any return from a constructor will end up here. */ + add_tree (build_min_nt (LABEL_STMT, ctor_label)); + + /* Clear CTOR_LABEL so that finish_return_stmt knows to really + generate the return, rather than a goto to CTOR_LABEL. */ + ctor_label = NULL_TREE; + /* In check_return_expr we translate an empty return from a + constructor to a return of `this'. */ + finish_return_stmt (NULL_TREE); +} + /* At the end of every destructor we generate code to restore virtual function tables to the values desired by base classes and to call - to base class destructors. Do that now, for DECL. */ + to base class destructors. Do that now. */ static void finish_destructor_body () @@ -13344,6 +13365,9 @@ finish_destructor_body () /* Create a block to contain all the extra code. */ compound_stmt = begin_compound_stmt (/*has_no_scope=*/0); + /* Any return from a destructor will end up here. */ + add_tree (build_min_nt (LABEL_STMT, dtor_label)); + /* Generate the code to call destructor on base class. If this destructor belongs to a class with virtual functions, then set the virtual function table pointer to represent the type of our @@ -13372,13 +13396,12 @@ finish_destructor_body () || TREE_OPERAND (exprstmt, 0) != integer_zero_node || TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))) { - add_tree (build_min_nt (LABEL_STMT, dtor_label)); if (exprstmt != void_zero_node) /* Don't call `expand_expr_stmt' if we're not going to do anything, since -Wall will give a diagnostic. */ finish_expr_stmt (exprstmt); - /* Run destructor on all virtual baseclasses. */ + /* Run destructors for all virtual baseclasses. */ if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)) { tree vbases = nreverse (copy_list (CLASSTYPE_VBASECLASSES (current_class_type))); @@ -13496,10 +13519,23 @@ finish_function (lineno, flags) if (building_stmt_tree ()) { - if (DECL_CONSTRUCTOR_P (fndecl) && call_poplevel) - do_poplevel (); + if (DECL_CONSTRUCTOR_P (fndecl)) + { + finish_constructor_body (); + if (call_poplevel) + do_poplevel (); + } else if (DECL_DESTRUCTOR_P (fndecl) && !processing_template_decl) finish_destructor_body (); + else if (DECL_MAIN_P (fndecl)) + { + /* Make it so that `main' always returns 0 by default. */ +#ifdef VMS + finish_return_stmt (integer_one_node); +#else + finish_return_stmt (integer_zero_node); +#endif + } /* Finish dealing with exception specifiers. */ if (flag_exceptions && !processing_template_decl @@ -13535,28 +13571,11 @@ finish_function (lineno, flags) ; else if (DECL_CONSTRUCTOR_P (fndecl)) { - /* This is where the body of the constructor begins. All - subobjects have been fully constructed at this point. */ + /* All subobjects have been fully constructed at this point. */ end_protect_partials (); - /* This is where the body of the constructor ends. */ - expand_label (ctor_label); - ctor_label = NULL_TREE; - if (call_poplevel) do_poplevel (); - - /* c_expand_return knows to return 'this' from a constructor. */ - c_expand_return (NULL_TREE); - } - else if (DECL_MAIN_P (fndecl)) - { - /* Make it so that `main' always returns 0 by default. */ -#ifdef VMS - c_expand_return (integer_one_node); -#else - c_expand_return (integer_zero_node); -#endif } else if (return_label != NULL_RTX && flag_this_is_variable <= 0 diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index cd48570..ac7856b 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -371,6 +371,33 @@ void finish_return_stmt (expr) tree expr; { + if (doing_semantic_analysis_p () && !processing_template_decl) + expr = check_return_expr (expr); + + if (doing_semantic_analysis_p () && !processing_template_decl) + { + if (DECL_CONSTRUCTOR_P (current_function_decl) && ctor_label) + { + /* Even returns without a value in a constructor must return + `this'. We accomplish this by sending all returns in a + constructor to the CTOR_LABEL; finish_function emits code to + return a value there. When we finally generate the real + return statement, CTOR_LABEL is no longer set, and we fall + through into the normal return-processing code below. */ + finish_goto_stmt (ctor_label); + return; + } + else if (DECL_DESTRUCTOR_P (current_function_decl)) + { + /* Similarly, all destructors must run destructors for + base-classes before returning. So, all returns in a + destructor get sent to the DTOR_LABEL; finsh_function emits + code to return a value there. */ + finish_goto_stmt (dtor_label); + return; + } + } + if (building_stmt_tree ()) add_tree (build_min_nt (RETURN_STMT, expr)); else diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 80be5f8..6a18b11 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -64,6 +64,7 @@ static tree get_delta_difference PROTO((tree, tree, int)); static int comp_cv_target_types PROTO((tree, tree, int)); static void casts_away_constness_r PROTO((tree *, tree *)); static int casts_away_constness PROTO ((tree, tree)); +static void maybe_warn_about_returning_address_of_local PROTO ((tree)); /* Return the target type of TYPE, which means return T for: T*, T&, T[], T (...), and otherwise, just T. */ @@ -6639,41 +6640,103 @@ c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) emit_queue (); } -/* Expand a C `return' statement. - RETVAL is the expression for what to return, - or a null pointer for `return;' with no value. +/* If RETVAL is the address of, or a reference to, a local variable or + temporary give an appropraite warning. */ - C++: upon seeing a `return', we must call destructors on all - variables in scope which had constructors called on them. - This means that if in a destructor, the base class destructors - must be called before returning. +static void +maybe_warn_about_returning_address_of_local (retval) + tree retval; +{ + tree valtype = TREE_TYPE (DECL_RESULT (current_function_decl)); - The RETURN statement in C++ has initialization semantics. */ + if (TREE_CODE (valtype) == REFERENCE_TYPE) + { + tree whats_returned; -void -c_expand_return (retval) + /* Sort through common things to see what it is + we are returning. */ + whats_returned = retval; + if (TREE_CODE (whats_returned) == COMPOUND_EXPR) + { + whats_returned = TREE_OPERAND (whats_returned, 1); + if (TREE_CODE (whats_returned) == ADDR_EXPR) + whats_returned = TREE_OPERAND (whats_returned, 0); + } + while (TREE_CODE (whats_returned) == CONVERT_EXPR + || TREE_CODE (whats_returned) == NOP_EXPR) + whats_returned = TREE_OPERAND (whats_returned, 0); + if (TREE_CODE (whats_returned) == ADDR_EXPR) + { + whats_returned = TREE_OPERAND (whats_returned, 0); + while (TREE_CODE (whats_returned) == AGGR_INIT_EXPR + || TREE_CODE (whats_returned) == TARGET_EXPR) + { + /* Get the target. */ + whats_returned = TREE_OPERAND (whats_returned, 0); + warning ("returning reference to temporary"); + } + } + + if (TREE_CODE (whats_returned) == VAR_DECL + && DECL_NAME (whats_returned)) + { + if (TEMP_NAME_P (DECL_NAME (whats_returned))) + warning ("reference to non-lvalue returned"); + else if (TREE_CODE (TREE_TYPE (whats_returned)) != REFERENCE_TYPE + && DECL_FUNCTION_SCOPE_P (whats_returned) + && !(TREE_STATIC (whats_returned) + || TREE_PUBLIC (whats_returned))) + cp_warning_at ("reference to local variable `%D' returned", + whats_returned); + } + } + else if (TREE_CODE (retval) == ADDR_EXPR) + { + tree whats_returned = TREE_OPERAND (retval, 0); + + if (TREE_CODE (whats_returned) == VAR_DECL + && DECL_NAME (whats_returned) + && DECL_FUNCTION_SCOPE_P (whats_returned) + && !(TREE_STATIC (whats_returned) + || TREE_PUBLIC (whats_returned))) + cp_warning_at ("address of local variable `%D' returned", + whats_returned); + } +} + +/* Check that returning RETVAL from the current function is legal. + Return an expression explicitly showing all conversions required to + change RETVAL into the function return type, and to assign it to + the DECL_RESULT for the function. */ + +tree +check_return_expr (retval) tree retval; { - tree result = DECL_RESULT (current_function_decl); - tree valtype = TREE_TYPE (result); - + tree result; + /* The type actually returned by the function, after any + promotions. */ + tree valtype; + int fn_returns_value_p; + + /* A `volatile' function is one that isn't supposed to return, ever. + (This is a G++ extension, used to get better code for functions + that call the `volatile' function.) */ if (TREE_THIS_VOLATILE (current_function_decl)) warning ("function declared `noreturn' has a `return' statement"); + /* Check for various simple errors. */ if (retval == error_mark_node) { + /* If an error occurred, there's nothing to do. */ current_function_returns_null = 1; - return; + return error_mark_node; } - - if (dtor_label) + else if (dtor_label) { if (retval) error ("returning a value from a destructor"); - - /* Can't just return from a destructor. */ - expand_goto (dtor_label); - return; + return NULL_TREE; } else if (in_function_try_handler && DECL_CONSTRUCTOR_P (current_function_decl)) @@ -6681,8 +6744,57 @@ c_expand_return (retval) /* If a return statement appears in a handler of the function-try-block of a constructor, the program is ill-formed. */ error ("cannot return from a handler of a function-try-block of a constructor"); - return; + return error_mark_node; + } + else if (retval && DECL_CONSTRUCTOR_P (current_function_decl)) + /* You can't return a value from a constructor. */ + error ("returning a value from a constructor"); + + /* Constructors actually always return `this', even though in C++ + you can't return a value from a constructor. */ + if (DECL_CONSTRUCTOR_P (current_function_decl)) + retval = current_class_ptr; + + /* When no explicit return-value is given in a function with a named + return value, the named return value is used. */ + result = DECL_RESULT (current_function_decl); + valtype = TREE_TYPE (result); + my_friendly_assert (valtype != NULL_TREE, 19990924); + fn_returns_value_p = !same_type_p (valtype, void_type_node); + if (!retval && DECL_NAME (result) && fn_returns_value_p) + retval = result; + + /* Check for a return statement with no return value in a function + that's supposed to return a value. */ + if (!retval && fn_returns_value_p) + { + pedwarn ("`return' with no value, in function returning non-void"); + /* Clear this, so finish_function won't say that we reach the + end of a non-void function (which we don't, we gave a + return!). */ + current_function_returns_null = 0; + } + /* Check for a return statement with a value in a function that + isn't supposed to return a value. */ + else if (retval && !fn_returns_value_p) + { + if (same_type_p (TREE_TYPE (retval), void_type_node)) + /* You can return a `void' value from a function of `void' + type. In that case, we have to evaluate the expression for + its side-effects. */ + finish_expr_stmt (retval); + else + pedwarn ("`return' with a value, in function returning void"); + + current_function_returns_null = 1; + + /* There's really no value to return, after all. */ + return NULL_TREE; } + else if (!retval) + /* Remember that this function can sometimes return without a + value. */ + current_function_returns_null = 1; /* Only operator new(...) throw(), can return NULL [expr.new/13]. */ if ((DECL_NAME (current_function_decl) == ansi_opname[(int) NEW_EXPR] @@ -6690,41 +6802,6 @@ c_expand_return (retval) && !TYPE_NOTHROW_P (TREE_TYPE (current_function_decl)) && null_ptr_cst_p (retval)) cp_warning ("operator new should throw an exception, not return NULL"); - - if (retval == NULL_TREE) - { - /* A non-named return value does not count. */ - - if (DECL_CONSTRUCTOR_P (current_function_decl)) - retval = current_class_ptr; - else if (DECL_NAME (result) != NULL_TREE - && TREE_CODE (valtype) != VOID_TYPE) - retval = result; - else - { - current_function_returns_null = 1; - - if (valtype != NULL_TREE && TREE_CODE (valtype) != VOID_TYPE) - { - if (DECL_NAME (DECL_RESULT (current_function_decl)) == NULL_TREE) - { - pedwarn ("`return' with no value, in function returning non-void"); - /* Clear this, so finish_function won't say that we - reach the end of a non-void function (which we don't, - we gave a return!). */ - current_function_returns_null = 0; - } - } - - expand_null_return (); - return; - } - } - else if (DECL_CONSTRUCTOR_P (current_function_decl)) - { - error ("returning a value from a constructor"); - retval = current_class_ptr; - } /* Effective C++ rule 15. See also start_function. */ if (warn_ecpp @@ -6732,137 +6809,76 @@ c_expand_return (retval) && retval != current_class_ref) cp_warning ("`operator=' should return a reference to `*this'"); - if (valtype == NULL_TREE || TREE_CODE (valtype) == VOID_TYPE) - { - current_function_returns_null = 1; - if (TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE) - pedwarn ("`return' with a value, in function returning void"); - expand_return (retval); - return; - } - - /* Now deal with possible C++ hair: - (1) Compute the return value. - (2) If there are aggregate values with destructors which - must be cleaned up, clean them (taking care - not to clobber the return value). - (3) If an X(X&) constructor is defined, the return - value must be returned via that. */ - - if (retval == result - || DECL_CONSTRUCTOR_P (current_function_decl)) - /* It's already done for us. */; - else if (TREE_CODE (TREE_TYPE (retval)) == VOID_TYPE) - { - pedwarn ("return of void value in function returning non-void"); - expand_expr_stmt (retval); - retval = 0; - } + /* We don't need to do any conversions when there's nothing being + returned. */ + if (!retval) + return NULL_TREE; + + /* Do any required conversions. */ + if (retval == result || DECL_CONSTRUCTOR_P (current_function_decl)) + /* No conversions are required. */ + ; else { + /* The type the function is declared to return. */ tree functype = TREE_TYPE (TREE_TYPE (current_function_decl)); /* First convert the value to the function's return type, then to the type of return value's location to handle the case that functype is thiner than the valtype. */ - retval = convert_for_initialization (NULL_TREE, functype, retval, LOOKUP_NORMAL|LOOKUP_ONLYCONVERTING, "return", NULL_TREE, 0); - retval = convert (valtype, retval); + /* If the conversion failed, treat this just like `return;'. */ if (retval == error_mark_node) - { - /* Avoid warning about control reaching end of function. */ - expand_null_return (); - return; - } - + return NULL_TREE; /* We can't initialize a register from a AGGR_INIT_EXPR. */ else if (! current_function_returns_struct && TREE_CODE (retval) == TARGET_EXPR && TREE_CODE (TREE_OPERAND (retval, 1)) == AGGR_INIT_EXPR) retval = build (COMPOUND_EXPR, TREE_TYPE (retval), retval, TREE_OPERAND (retval, 0)); - - /* Add some useful error checking for C++. */ - else if (TREE_CODE (valtype) == REFERENCE_TYPE) - { - tree whats_returned; - - /* Sort through common things to see what it is - we are returning. */ - whats_returned = retval; - if (TREE_CODE (whats_returned) == COMPOUND_EXPR) - { - whats_returned = TREE_OPERAND (whats_returned, 1); - if (TREE_CODE (whats_returned) == ADDR_EXPR) - whats_returned = TREE_OPERAND (whats_returned, 0); - } - while (TREE_CODE (whats_returned) == CONVERT_EXPR - || TREE_CODE (whats_returned) == NOP_EXPR) - whats_returned = TREE_OPERAND (whats_returned, 0); - if (TREE_CODE (whats_returned) == ADDR_EXPR) - { - whats_returned = TREE_OPERAND (whats_returned, 0); - while (TREE_CODE (whats_returned) == AGGR_INIT_EXPR - || TREE_CODE (whats_returned) == TARGET_EXPR) - { - /* Get the target. */ - whats_returned = TREE_OPERAND (whats_returned, 0); - warning ("returning reference to temporary"); - } - } - - if (TREE_CODE (whats_returned) == VAR_DECL && DECL_NAME (whats_returned)) - { - if (TEMP_NAME_P (DECL_NAME (whats_returned))) - warning ("reference to non-lvalue returned"); - else if (TREE_CODE (TREE_TYPE (whats_returned)) != REFERENCE_TYPE - && DECL_FUNCTION_SCOPE_P (whats_returned) - && !(TREE_STATIC (whats_returned) - || TREE_PUBLIC (whats_returned))) - cp_warning_at ("reference to local variable `%D' returned", whats_returned); - } - } - else if (TREE_CODE (retval) == ADDR_EXPR) - { - tree whats_returned = TREE_OPERAND (retval, 0); - - if (TREE_CODE (whats_returned) == VAR_DECL - && DECL_NAME (whats_returned) - && DECL_FUNCTION_SCOPE_P (whats_returned) - && !(TREE_STATIC (whats_returned) - || TREE_PUBLIC (whats_returned))) - cp_warning_at ("address of local variable `%D' returned", whats_returned); - } - } - - if (retval != NULL_TREE - && TREE_CODE_CLASS (TREE_CODE (retval)) == 'd' - && ! in_control_zone_p ()) - current_function_return_value = retval; - - if (ctor_label && TREE_CODE (ctor_label) != ERROR_MARK) - { - /* Here RETVAL is CURRENT_CLASS_PTR, so there's nothing to do. */ - expand_goto (ctor_label); + else + maybe_warn_about_returning_address_of_local (retval); } - + + /* Actually copy the value returned into the appropriate location. */ if (retval && retval != result) { - result = build (INIT_EXPR, TREE_TYPE (result), result, retval); - TREE_SIDE_EFFECTS (result) = 1; + retval = build (INIT_EXPR, TREE_TYPE (result), result, retval); + TREE_SIDE_EFFECTS (retval) = 1; } - expand_start_target_temps (); + /* All done. Remember that this function did return a value. */ + current_function_returns_value = 1; + return retval; +} + +/* Expand a C `return' statement. + RETVAL is the expression for what to return, + or a null pointer for `return;' with no value. - expand_return (result); + C++: upon seeing a `return', we must call destructors on all + variables in scope which had constructors called on them. + This means that if in a destructor, the base class destructors + must be called before returning. - expand_end_target_temps (); + The RETURN statement in C++ has initialization semantics. */ - current_function_returns_value = 1; +void +c_expand_return (retval) + tree retval; +{ + if (!retval) + expand_null_return (); + else + { + expand_start_target_temps (); + expand_return (retval); + expand_end_target_temps (); + } } /* Start a C switch statement, testing expression EXP. |