Commit efee38a9 by Mark Mitchell Committed by Mark Mitchell

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
parent 5a657fc3
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.
......
......@@ -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));
......
......@@ -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
......
......@@ -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
......
......@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment