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
This commit is contained in:
Mark Mitchell 1999-09-25 18:10:04 +00:00 committed by Mark Mitchell
parent 5a657fc371
commit efee38a99a
5 changed files with 312 additions and 230 deletions

View File

@ -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.

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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,6 +6640,222 @@ c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
emit_queue ();
}
/* If RETVAL is the address of, or a reference to, a local variable or
temporary give an appropraite warning. */
static void
maybe_warn_about_returning_address_of_local (retval)
tree retval;
{
tree valtype = TREE_TYPE (DECL_RESULT (current_function_decl));
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);
}
}
/* 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;
/* 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 error_mark_node;
}
else if (dtor_label)
{
if (retval)
error ("returning a value from a destructor");
return NULL_TREE;
}
else if (in_function_try_handler
&& DECL_CONSTRUCTOR_P (current_function_decl))
{
/* 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 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]
|| DECL_NAME (current_function_decl) == ansi_opname[(int) VEC_NEW_EXPR])
&& !TYPE_NOTHROW_P (TREE_TYPE (current_function_decl))
&& null_ptr_cst_p (retval))
cp_warning ("operator new should throw an exception, not return NULL");
/* Effective C++ rule 15. See also start_function. */
if (warn_ecpp
&& DECL_NAME (current_function_decl) == ansi_opname[(int) MODIFY_EXPR]
&& retval != current_class_ref)
cp_warning ("`operator=' should return a reference to `*this'");
/* 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)
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));
else
maybe_warn_about_returning_address_of_local (retval);
}
/* Actually copy the value returned into the appropriate location. */
if (retval && retval != result)
{
retval = build (INIT_EXPR, TREE_TYPE (result), result, retval);
TREE_SIDE_EFFECTS (retval) = 1;
}
/* 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.
@ -6654,215 +6871,14 @@ void
c_expand_return (retval)
tree retval;
{
tree result = DECL_RESULT (current_function_decl);
tree valtype = TREE_TYPE (result);
if (TREE_THIS_VOLATILE (current_function_decl))
warning ("function declared `noreturn' has a `return' statement");
if (retval == error_mark_node)
{
current_function_returns_null = 1;
return;
}
if (dtor_label)
{
if (retval)
error ("returning a value from a destructor");
/* Can't just return from a destructor. */
expand_goto (dtor_label);
return;
}
else if (in_function_try_handler
&& DECL_CONSTRUCTOR_P (current_function_decl))
{
/* 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;
}
/* Only operator new(...) throw(), can return NULL [expr.new/13]. */
if ((DECL_NAME (current_function_decl) == ansi_opname[(int) NEW_EXPR]
|| DECL_NAME (current_function_decl) == ansi_opname[(int) VEC_NEW_EXPR])
&& !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
&& DECL_NAME (current_function_decl) == ansi_opname[(int) MODIFY_EXPR]
&& 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;
}
if (!retval)
expand_null_return ();
else
{
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 (retval == error_mark_node)
{
/* Avoid warning about control reaching end of function. */
expand_null_return ();
return;
}
/* 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);
}
expand_start_target_temps ();
expand_return (retval);
expand_end_target_temps ();
}
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);
}
if (retval && retval != result)
{
result = build (INIT_EXPR, TREE_TYPE (result), result, retval);
TREE_SIDE_EFFECTS (result) = 1;
}
expand_start_target_temps ();
expand_return (result);
expand_end_target_temps ();
current_function_returns_value = 1;
}
/* Start a C switch statement, testing expression EXP.