exp_ch5.adb (Expand_Assign_Array): If source or target of assignment is a variable that renames a slice...
2007-08-14 Ed Schonberg <schonberg@adacore.com> * exp_ch5.adb (Expand_Assign_Array): If source or target of assignment is a variable that renames a slice, use the variable itself in the expannsion when the renamed expression itself may be modified between the declaration of the renaming and the array assignment. From-SVN: r127430
This commit is contained in:
parent
fa57ac97e9
commit
bd28782c2f
@ -64,12 +64,6 @@ with Validsw; use Validsw;
|
||||
|
||||
package body Exp_Ch5 is
|
||||
|
||||
Enable_New_Return_Processing : constant Boolean := True;
|
||||
-- ??? This flag is temporary. False causes the compiler to use the old
|
||||
-- version of Analyze_Return_Statement; True, the new version, which does
|
||||
-- not yet work. We probably want this to match the corresponding thing
|
||||
-- in sem_ch6.adb.
|
||||
|
||||
function Change_Of_Representation (N : Node_Id) return Boolean;
|
||||
-- Determine if the right hand side of the assignment N is a type
|
||||
-- conversion which requires a change of representation. Called
|
||||
@ -110,17 +104,15 @@ package body Exp_Ch5 is
|
||||
-- of representation.
|
||||
|
||||
procedure Expand_Non_Function_Return (N : Node_Id);
|
||||
-- Called by Expand_Simple_Return in case we're returning from a procedure
|
||||
-- body, entry body, accept statement, or extended returns statement.
|
||||
-- Note that all non-function returns are simple return statements.
|
||||
-- Called by Expand_N_Simple_Return_Statement in case we're returning from
|
||||
-- a procedure body, entry body, accept statement, or extended return
|
||||
-- statement. Note that all non-function returns are simple return
|
||||
-- statements.
|
||||
|
||||
procedure Expand_Simple_Function_Return (N : Node_Id);
|
||||
-- Expand simple return from function. Called by Expand_Simple_Return in
|
||||
-- case we're returning from a function body.
|
||||
|
||||
procedure Expand_Simple_Return (N : Node_Id);
|
||||
-- Expansion for simple return statements. Calls either
|
||||
-- Expand_Simple_Function_Return or Expand_Non_Function_Return.
|
||||
-- Expand simple return from function. Called by
|
||||
-- Expand_N_Simple_Return_Statement in case we're returning from a function
|
||||
-- body.
|
||||
|
||||
function Make_Tag_Ctrl_Assignment (N : Node_Id) return List_Id;
|
||||
-- Generate the necessary code for controlled and tagged assignment,
|
||||
@ -179,7 +171,7 @@ package body Exp_Ch5 is
|
||||
-- This switch is set to True if the array move must be done using
|
||||
-- an explicit front end generated loop.
|
||||
|
||||
procedure Apply_Dereference (Arg : in out Node_Id);
|
||||
procedure Apply_Dereference (Arg : Node_Id);
|
||||
-- If the argument is an access to an array, and the assignment is
|
||||
-- converted into a procedure call, apply explicit dereference.
|
||||
|
||||
@ -202,7 +194,7 @@ package body Exp_Ch5 is
|
||||
-- Apply_Dereference --
|
||||
-----------------------
|
||||
|
||||
procedure Apply_Dereference (Arg : in out Node_Id) is
|
||||
procedure Apply_Dereference (Arg : Node_Id) is
|
||||
Typ : constant Entity_Id := Etype (Arg);
|
||||
begin
|
||||
if Is_Access_Type (Typ) then
|
||||
@ -260,31 +252,26 @@ package body Exp_Ch5 is
|
||||
-- Start of processing for Expand_Assign_Array
|
||||
|
||||
begin
|
||||
-- Deal with length check, note that the length check is done with
|
||||
-- Deal with length check. Note that the length check is done with
|
||||
-- respect to the right hand side as given, not a possible underlying
|
||||
-- renamed object, since this would generate incorrect extra checks.
|
||||
|
||||
Apply_Length_Check (Rhs, L_Type);
|
||||
|
||||
-- We start by assuming that the move can be done in either
|
||||
-- direction, i.e. that the two sides are completely disjoint.
|
||||
-- We start by assuming that the move can be done in either direction,
|
||||
-- i.e. that the two sides are completely disjoint.
|
||||
|
||||
Set_Forwards_OK (N, True);
|
||||
Set_Backwards_OK (N, True);
|
||||
|
||||
-- Normally it is only the slice case that can lead to overlap, and
|
||||
-- explicit checks for slices are made below. But there is one case
|
||||
-- where the slice can be implicit and invisible to us and that is the
|
||||
-- case where we have a one dimensional array, and either both operands
|
||||
-- are parameters, or one is a parameter and the other is a global
|
||||
-- variable. In this case the parameter could be a slice that overlaps
|
||||
-- with the other parameter.
|
||||
-- where the slice can be implicit and invisible to us: when we have a
|
||||
-- one dimensional array, and either both operands are parameters, or
|
||||
-- one is a parameter (which can be a slice passed by reference) and the
|
||||
-- other is a non-local variable. In this case the parameter could be a
|
||||
-- slice that overlaps with the other operand.
|
||||
|
||||
-- Check for the case of slices requiring an explicit loop. Normally it
|
||||
-- is only the explicit slice cases that bother us, but in the case of
|
||||
-- one dimensional arrays, parameters can be slices that are passed by
|
||||
-- reference, so we can have aliasing for assignments from one parameter
|
||||
-- to another, or assignments between parameters and nonlocal variables.
|
||||
-- However, if the array subtype is a constrained first subtype in the
|
||||
-- parameter case, then we don't have to worry about overlap, since
|
||||
-- slice assignments aren't possible (other than for a slice denoting
|
||||
@ -340,8 +327,8 @@ package body Exp_Ch5 is
|
||||
then
|
||||
Loop_Required := True;
|
||||
|
||||
-- Arrays with controlled components are expanded into a loop
|
||||
-- to force calls to adjust at the component level.
|
||||
-- Arrays with controlled components are expanded into a loop to force
|
||||
-- calls to Adjust at the component level.
|
||||
|
||||
elsif Has_Controlled_Component (L_Type) then
|
||||
Loop_Required := True;
|
||||
@ -378,8 +365,8 @@ package body Exp_Ch5 is
|
||||
-- do this, we get the wrong length computed for the array to be
|
||||
-- moved. The two cases we need to worry about are:
|
||||
|
||||
-- Explicit deference of an unconstrained packed array type as
|
||||
-- in the following example:
|
||||
-- Explicit deference of an unconstrained packed array type as in the
|
||||
-- following example:
|
||||
|
||||
-- procedure C52 is
|
||||
-- type BITS is array(INTEGER range <>) of BOOLEAN;
|
||||
@ -401,7 +388,7 @@ package body Exp_Ch5 is
|
||||
-- File.Storage := Contents;
|
||||
-- end Write_All;
|
||||
|
||||
-- We expand to a loop in either of these two cases
|
||||
-- We expand to a loop in either of these two cases.
|
||||
|
||||
-- Question for future thought. Another potentially more efficient
|
||||
-- approach would be to create the actual subtype, and then do an
|
||||
@ -411,7 +398,7 @@ package body Exp_Ch5 is
|
||||
|
||||
function Is_UBPA_Reference (Opnd : Node_Id) return Boolean;
|
||||
-- Function to perform required test for the first case, above
|
||||
-- (dereference of an unconstrained bit packed array)
|
||||
-- (dereference of an unconstrained bit packed array).
|
||||
|
||||
-----------------------
|
||||
-- Is_UBPA_Reference --
|
||||
@ -470,14 +457,14 @@ package body Exp_Ch5 is
|
||||
-- The back end can always handle the assignment if the right side is a
|
||||
-- string literal (note that overlap is definitely impossible in this
|
||||
-- case). If the type is packed, a string literal is always converted
|
||||
-- into aggregate, except in the case of a null slice, for which no
|
||||
-- into an aggregate, except in the case of a null slice, for which no
|
||||
-- aggregate can be written. In that case, rewrite the assignment as a
|
||||
-- null statement, a length check has already been emitted to verify
|
||||
-- that the range of the left-hand side is empty.
|
||||
|
||||
-- Note that this code is not executed if we had an assignment of a
|
||||
-- Note that this code is not executed if we have an assignment of a
|
||||
-- string literal to a non-bit aligned component of a record, a case
|
||||
-- which cannot be handled by the backend
|
||||
-- which cannot be handled by the backend.
|
||||
|
||||
elsif Nkind (Rhs) = N_String_Literal then
|
||||
if String_Length (Strval (Rhs)) = 0
|
||||
@ -600,8 +587,8 @@ package body Exp_Ch5 is
|
||||
end if;
|
||||
|
||||
-- If both sides are slices, we must figure out whether it is safe
|
||||
-- to do the move in one direction or the other It is always safe if
|
||||
-- there is a change of representation since obviously two arrays
|
||||
-- to do the move in one direction or the other. It is always safe
|
||||
-- if there is a change of representation since obviously two arrays
|
||||
-- with different representations cannot possibly overlap.
|
||||
|
||||
if (not Crep) and L_Slice and R_Slice then
|
||||
@ -708,6 +695,31 @@ package body Exp_Ch5 is
|
||||
-- <code for Backwards_OK = True above>
|
||||
-- end if;
|
||||
|
||||
-- In order to detect possible aliasing, we examine the renamed
|
||||
-- expression when the source or target is a renaming. However,
|
||||
-- the renaming may be intended to capture an address that may be
|
||||
-- affected by subsequent code, and therefore we must recover
|
||||
-- the actual entity for the expansion that follows, not the
|
||||
-- object it renames. In particular, if source or target designate
|
||||
-- a portion of a dynamically allocated object, the pointer to it
|
||||
-- may be reassigned but the renaming preserves the proper location.
|
||||
|
||||
if Is_Entity_Name (Rhs)
|
||||
and then
|
||||
Nkind (Parent (Entity (Rhs))) = N_Object_Renaming_Declaration
|
||||
and then Nkind (Act_Rhs) = N_Slice
|
||||
then
|
||||
Rarray := Rhs;
|
||||
end if;
|
||||
|
||||
if Is_Entity_Name (Lhs)
|
||||
and then
|
||||
Nkind (Parent (Entity (Lhs))) = N_Object_Renaming_Declaration
|
||||
and then Nkind (Act_Lhs) = N_Slice
|
||||
then
|
||||
Larray := Lhs;
|
||||
end if;
|
||||
|
||||
-- Cases where either Forwards_OK or Backwards_OK is true
|
||||
|
||||
if Forwards_OK (N) or else Backwards_OK (N) then
|
||||
@ -1697,7 +1709,7 @@ package body Exp_Ch5 is
|
||||
|
||||
begin
|
||||
C_Es :=
|
||||
Range_Check
|
||||
Get_Range_Checks
|
||||
(Lhs,
|
||||
Target_Typ,
|
||||
Etype (Designated_Type (Etype (Lhs))));
|
||||
@ -2340,9 +2352,8 @@ package body Exp_Ch5 is
|
||||
|
||||
-- That is, we need to have a reified return object if there are statements
|
||||
-- (which might refer to it) or if we're doing build-in-place (so we can
|
||||
-- set its address to the final resting place -- but that key part is not
|
||||
-- yet implemented) or if there is no expression (in which case default
|
||||
-- initial values might need to be set).
|
||||
-- set its address to the final resting place or if there is no expression
|
||||
-- (in which case default initial values might need to be set).
|
||||
|
||||
procedure Expand_N_Extended_Return_Statement (N : Node_Id) is
|
||||
Loc : constant Source_Ptr := Sloc (N);
|
||||
@ -2420,21 +2431,25 @@ package body Exp_Ch5 is
|
||||
---------------------
|
||||
|
||||
function Move_Final_List return Node_Id is
|
||||
Flist : constant Entity_Id :=
|
||||
Finalization_Chain_Entity
|
||||
(Return_Statement_Entity (N));
|
||||
Flist : constant Entity_Id :=
|
||||
Finalization_Chain_Entity (Return_Statement_Entity (N));
|
||||
|
||||
From : constant Node_Id :=
|
||||
New_Reference_To (Flist, Loc);
|
||||
From : constant Node_Id := New_Reference_To (Flist, Loc);
|
||||
|
||||
Caller_Final_List : constant Entity_Id :=
|
||||
Build_In_Place_Formal
|
||||
(Parent_Function, BIP_Final_List);
|
||||
|
||||
To : constant Node_Id :=
|
||||
New_Reference_To (Caller_Final_List, Loc);
|
||||
To : constant Node_Id := New_Reference_To (Caller_Final_List, Loc);
|
||||
|
||||
begin
|
||||
-- Catch cases where a finalization chain entity has not been
|
||||
-- associated with the return statement entity.
|
||||
|
||||
pragma Assert (Present (Flist));
|
||||
|
||||
-- Build required call
|
||||
|
||||
return
|
||||
Make_If_Statement (Loc,
|
||||
Condition =>
|
||||
@ -2526,7 +2541,7 @@ package body Exp_Ch5 is
|
||||
-- Build a simple_return_statement that returns the return object
|
||||
|
||||
Return_Stm :=
|
||||
Make_Return_Statement (Loc,
|
||||
Make_Simple_Return_Statement (Loc,
|
||||
Expression => New_Occurrence_Of (Return_Object_Entity, Loc));
|
||||
Append_To (Statements, Return_Stm);
|
||||
|
||||
@ -2926,7 +2941,7 @@ package body Exp_Ch5 is
|
||||
|
||||
-- Build simple_return_statement that returns the expression directly
|
||||
|
||||
Return_Stm := Make_Return_Statement (Loc, Expression => Exp);
|
||||
Return_Stm := Make_Simple_Return_Statement (Loc, Expression => Exp);
|
||||
|
||||
Result := Return_Stm;
|
||||
end if;
|
||||
@ -2991,6 +3006,12 @@ package body Exp_Ch5 is
|
||||
E : Node_Id;
|
||||
New_If : Node_Id;
|
||||
|
||||
Warn_If_Deleted : constant Boolean :=
|
||||
Warn_On_Deleted_Code and then Comes_From_Source (N);
|
||||
-- Indicates whether we want warnings when we delete branches of the
|
||||
-- if statement based on constant condition analysis. We never want
|
||||
-- these warnings for expander generated code.
|
||||
|
||||
begin
|
||||
Adjust_Condition (Condition (N));
|
||||
|
||||
@ -3007,8 +3028,8 @@ package body Exp_Ch5 is
|
||||
|
||||
-- All the else parts can be killed
|
||||
|
||||
Kill_Dead_Code (Elsif_Parts (N), Warn_On_Deleted_Code);
|
||||
Kill_Dead_Code (Else_Statements (N), Warn_On_Deleted_Code);
|
||||
Kill_Dead_Code (Elsif_Parts (N), Warn_If_Deleted);
|
||||
Kill_Dead_Code (Else_Statements (N), Warn_If_Deleted);
|
||||
|
||||
Hed := Remove_Head (Then_Statements (N));
|
||||
Insert_List_After (N, Then_Statements (N));
|
||||
@ -3028,7 +3049,7 @@ package body Exp_Ch5 is
|
||||
Kill_Dead_Code (Condition (N));
|
||||
end if;
|
||||
|
||||
Kill_Dead_Code (Then_Statements (N), Warn_On_Deleted_Code);
|
||||
Kill_Dead_Code (Then_Statements (N), Warn_If_Deleted);
|
||||
|
||||
-- If there are no elsif statements, then we simply replace the
|
||||
-- entire if statement by the sequence of else statements.
|
||||
@ -3173,9 +3194,9 @@ package body Exp_Ch5 is
|
||||
Else_Stm : constant Node_Id := First (Else_Statements (N));
|
||||
|
||||
begin
|
||||
if Nkind (Then_Stm) = N_Return_Statement
|
||||
if Nkind (Then_Stm) = N_Simple_Return_Statement
|
||||
and then
|
||||
Nkind (Else_Stm) = N_Return_Statement
|
||||
Nkind (Else_Stm) = N_Simple_Return_Statement
|
||||
then
|
||||
declare
|
||||
Then_Expr : constant Node_Id := Expression (Then_Stm);
|
||||
@ -3190,7 +3211,7 @@ package body Exp_Ch5 is
|
||||
and then Entity (Else_Expr) = Standard_False
|
||||
then
|
||||
Rewrite (N,
|
||||
Make_Return_Statement (Loc,
|
||||
Make_Simple_Return_Statement (Loc,
|
||||
Expression => Relocate_Node (Condition (N))));
|
||||
Analyze (N);
|
||||
return;
|
||||
@ -3199,7 +3220,7 @@ package body Exp_Ch5 is
|
||||
and then Entity (Else_Expr) = Standard_True
|
||||
then
|
||||
Rewrite (N,
|
||||
Make_Return_Statement (Loc,
|
||||
Make_Simple_Return_Statement (Loc,
|
||||
Expression =>
|
||||
Make_Op_Not (Loc,
|
||||
Right_Opnd => Relocate_Node (Condition (N)))));
|
||||
@ -3412,430 +3433,35 @@ package body Exp_Ch5 is
|
||||
end if;
|
||||
end Expand_N_Loop_Statement;
|
||||
|
||||
-------------------------------
|
||||
-- Expand_N_Return_Statement --
|
||||
-------------------------------
|
||||
|
||||
procedure Expand_N_Return_Statement (N : Node_Id) is
|
||||
Loc : constant Source_Ptr := Sloc (N);
|
||||
Exp : constant Node_Id := Expression (N);
|
||||
Exptyp : Entity_Id;
|
||||
T : Entity_Id;
|
||||
Utyp : Entity_Id;
|
||||
Scope_Id : Entity_Id;
|
||||
Kind : Entity_Kind;
|
||||
Call : Node_Id;
|
||||
Acc_Stat : Node_Id;
|
||||
Goto_Stat : Node_Id;
|
||||
Lab_Node : Node_Id;
|
||||
Cur_Idx : Nat;
|
||||
Return_Type : Entity_Id;
|
||||
Result_Exp : Node_Id;
|
||||
Result_Id : Entity_Id;
|
||||
Result_Obj : Node_Id;
|
||||
--------------------------------------
|
||||
-- Expand_N_Simple_Return_Statement --
|
||||
--------------------------------------
|
||||
|
||||
procedure Expand_N_Simple_Return_Statement (N : Node_Id) is
|
||||
begin
|
||||
if Enable_New_Return_Processing then -- ???Temporary hack
|
||||
Expand_Simple_Return (N);
|
||||
return;
|
||||
end if;
|
||||
-- Distinguish the function and non-function cases:
|
||||
|
||||
-- Case where returned expression is present
|
||||
case Ekind (Return_Applies_To (Return_Statement_Entity (N))) is
|
||||
|
||||
if Present (Exp) then
|
||||
when E_Function |
|
||||
E_Generic_Function =>
|
||||
Expand_Simple_Function_Return (N);
|
||||
|
||||
-- Always normalize C/Fortran boolean result. This is not always
|
||||
-- necessary, but it seems a good idea to minimize the passing
|
||||
-- around of non-normalized values, and in any case this handles
|
||||
-- the processing of barrier functions for protected types, which
|
||||
-- turn the condition into a return statement.
|
||||
when E_Procedure |
|
||||
E_Generic_Procedure |
|
||||
E_Entry |
|
||||
E_Entry_Family |
|
||||
E_Return_Statement =>
|
||||
Expand_Non_Function_Return (N);
|
||||
|
||||
Exptyp := Etype (Exp);
|
||||
|
||||
if Is_Boolean_Type (Exptyp)
|
||||
and then Nonzero_Is_True (Exptyp)
|
||||
then
|
||||
Adjust_Condition (Exp);
|
||||
Adjust_Result_Type (Exp, Exptyp);
|
||||
end if;
|
||||
|
||||
-- Do validity check if enabled for returns
|
||||
|
||||
if Validity_Checks_On
|
||||
and then Validity_Check_Returns
|
||||
then
|
||||
Ensure_Valid (Exp);
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Find relevant enclosing scope from which return is returning
|
||||
|
||||
Cur_Idx := Scope_Stack.Last;
|
||||
loop
|
||||
Scope_Id := Scope_Stack.Table (Cur_Idx).Entity;
|
||||
|
||||
if Ekind (Scope_Id) /= E_Block
|
||||
and then Ekind (Scope_Id) /= E_Loop
|
||||
then
|
||||
exit;
|
||||
|
||||
else
|
||||
Cur_Idx := Cur_Idx - 1;
|
||||
pragma Assert (Cur_Idx >= 0);
|
||||
end if;
|
||||
end loop;
|
||||
-- ???I believe the above code is no longer necessary
|
||||
pragma Assert (Scope_Id =
|
||||
Return_Applies_To (Return_Statement_Entity (N)));
|
||||
|
||||
if No (Exp) then
|
||||
Kind := Ekind (Scope_Id);
|
||||
|
||||
-- If it is a return from procedures do no extra steps
|
||||
|
||||
if Kind = E_Procedure or else Kind = E_Generic_Procedure then
|
||||
return;
|
||||
end if;
|
||||
|
||||
pragma Assert (Is_Entry (Scope_Id));
|
||||
|
||||
-- Look at the enclosing block to see whether the return is from an
|
||||
-- accept statement or an entry body.
|
||||
|
||||
for J in reverse 0 .. Cur_Idx loop
|
||||
Scope_Id := Scope_Stack.Table (J).Entity;
|
||||
exit when Is_Concurrent_Type (Scope_Id);
|
||||
end loop;
|
||||
|
||||
-- If it is a return from accept statement it should be expanded
|
||||
-- as a call to RTS Complete_Rendezvous and a goto to the end of
|
||||
-- the accept body.
|
||||
|
||||
-- (cf : Expand_N_Accept_Statement, Expand_N_Selective_Accept,
|
||||
-- Expand_N_Accept_Alternative in exp_ch9.adb)
|
||||
|
||||
if Is_Task_Type (Scope_Id) then
|
||||
|
||||
Call := (Make_Procedure_Call_Statement (Loc,
|
||||
Name => New_Reference_To
|
||||
(RTE (RE_Complete_Rendezvous), Loc)));
|
||||
Insert_Before (N, Call);
|
||||
-- why not insert actions here???
|
||||
Analyze (Call);
|
||||
|
||||
Acc_Stat := Parent (N);
|
||||
while Nkind (Acc_Stat) /= N_Accept_Statement loop
|
||||
Acc_Stat := Parent (Acc_Stat);
|
||||
end loop;
|
||||
|
||||
Lab_Node := Last (Statements
|
||||
(Handled_Statement_Sequence (Acc_Stat)));
|
||||
|
||||
Goto_Stat := Make_Goto_Statement (Loc,
|
||||
Name => New_Occurrence_Of
|
||||
(Entity (Identifier (Lab_Node)), Loc));
|
||||
|
||||
Set_Analyzed (Goto_Stat);
|
||||
|
||||
Rewrite (N, Goto_Stat);
|
||||
Analyze (N);
|
||||
|
||||
-- If it is a return from an entry body, put a Complete_Entry_Body
|
||||
-- call in front of the return.
|
||||
|
||||
elsif Is_Protected_Type (Scope_Id) then
|
||||
|
||||
Call :=
|
||||
Make_Procedure_Call_Statement (Loc,
|
||||
Name => New_Reference_To
|
||||
(RTE (RE_Complete_Entry_Body), Loc),
|
||||
Parameter_Associations => New_List
|
||||
(Make_Attribute_Reference (Loc,
|
||||
Prefix =>
|
||||
New_Reference_To
|
||||
(Object_Ref
|
||||
(Corresponding_Body (Parent (Scope_Id))),
|
||||
Loc),
|
||||
Attribute_Name => Name_Unchecked_Access)));
|
||||
|
||||
Insert_Before (N, Call);
|
||||
Analyze (Call);
|
||||
end if;
|
||||
|
||||
return;
|
||||
end if;
|
||||
|
||||
T := Etype (Exp);
|
||||
Return_Type := Etype (Scope_Id);
|
||||
Utyp := Underlying_Type (Return_Type);
|
||||
|
||||
-- Check the result expression of a scalar function against the subtype
|
||||
-- of the function by inserting a conversion. This conversion must
|
||||
-- eventually be performed for other classes of types, but for now it's
|
||||
-- only done for scalars. ???
|
||||
|
||||
if Is_Scalar_Type (T) then
|
||||
Rewrite (Exp, Convert_To (Return_Type, Exp));
|
||||
Analyze (Exp);
|
||||
end if;
|
||||
|
||||
-- Deal with returning variable length objects and controlled types
|
||||
|
||||
-- Nothing to do if we are returning by reference, or this is not type
|
||||
-- that requires special processing (indicated by the fact that it
|
||||
-- requires a cleanup scope for the secondary stack case).
|
||||
|
||||
if Is_Inherently_Limited_Type (T) then
|
||||
null;
|
||||
|
||||
elsif not Requires_Transient_Scope (Return_Type) then
|
||||
|
||||
-- Mutable records with no variable length components are not
|
||||
-- returned on the sec-stack, so we need to make sure that the
|
||||
-- backend will only copy back the size of the actual value, and not
|
||||
-- the maximum size. We create an actual subtype for this purpose.
|
||||
|
||||
declare
|
||||
Ubt : constant Entity_Id := Underlying_Type (Base_Type (T));
|
||||
Decl : Node_Id;
|
||||
Ent : Entity_Id;
|
||||
|
||||
begin
|
||||
if Has_Discriminants (Ubt)
|
||||
and then not Is_Constrained (Ubt)
|
||||
and then not Has_Unchecked_Union (Ubt)
|
||||
then
|
||||
Decl := Build_Actual_Subtype (Ubt, Exp);
|
||||
Ent := Defining_Identifier (Decl);
|
||||
Insert_Action (Exp, Decl);
|
||||
|
||||
Rewrite (Exp, Unchecked_Convert_To (Ent, Exp));
|
||||
Analyze_And_Resolve (Exp);
|
||||
end if;
|
||||
end;
|
||||
|
||||
-- Here if secondary stack is used
|
||||
|
||||
else
|
||||
-- Make sure that no surrounding block will reclaim the secondary
|
||||
-- stack on which we are going to put the result. Not only may this
|
||||
-- introduce secondary stack leaks but worse, if the reclamation is
|
||||
-- done too early, then the result we are returning may get
|
||||
-- clobbered. See example in 7417-003.
|
||||
|
||||
declare
|
||||
S : Entity_Id := Current_Scope;
|
||||
|
||||
begin
|
||||
while Ekind (S) = E_Block or else Ekind (S) = E_Loop loop
|
||||
Set_Sec_Stack_Needed_For_Return (S, True);
|
||||
S := Enclosing_Dynamic_Scope (S);
|
||||
end loop;
|
||||
end;
|
||||
|
||||
-- Optimize the case where the result is a function call. In this
|
||||
-- case either the result is already on the secondary stack, or is
|
||||
-- already being returned with the stack pointer depressed and no
|
||||
-- further processing is required except to set the By_Ref flag to
|
||||
-- ensure that gigi does not attempt an extra unnecessary copy
|
||||
-- (actually not just unnecessary but harmfully wrong in the case of
|
||||
-- a controlled type, where gigi does not know how to do a copy). To
|
||||
-- make up for a gcc 2.8.1 deficiency (???), we perform the copy for
|
||||
-- array types if the constrained status of the target type is
|
||||
-- different from that of the expression.
|
||||
|
||||
if Requires_Transient_Scope (T)
|
||||
and then
|
||||
(not Is_Array_Type (T)
|
||||
or else Is_Constrained (T) = Is_Constrained (Return_Type)
|
||||
or else Is_Class_Wide_Type (Utyp)
|
||||
or else Controlled_Type (T))
|
||||
and then Nkind (Exp) = N_Function_Call
|
||||
then
|
||||
Set_By_Ref (N);
|
||||
|
||||
-- Remove side effects from the expression now so that other parts
|
||||
-- of the expander do not have to reanalyze the node without this
|
||||
-- optimization.
|
||||
|
||||
Rewrite (Exp, Duplicate_Subexpr_No_Checks (Exp));
|
||||
|
||||
-- For controlled types, do the allocation on the secondary stack
|
||||
-- manually in order to call adjust at the right time:
|
||||
|
||||
-- type Anon1 is access Return_Type;
|
||||
-- for Anon1'Storage_pool use ss_pool;
|
||||
-- Anon2 : anon1 := new Return_Type'(expr);
|
||||
-- return Anon2.all;
|
||||
|
||||
-- We do the same for classwide types that are not potentially
|
||||
-- controlled (by the virtue of restriction No_Finalization) because
|
||||
-- gigi is not able to properly allocate class-wide types.
|
||||
|
||||
elsif CW_Or_Controlled_Type (Utyp) then
|
||||
declare
|
||||
Loc : constant Source_Ptr := Sloc (N);
|
||||
Temp : constant Entity_Id :=
|
||||
Make_Defining_Identifier (Loc,
|
||||
Chars => New_Internal_Name ('R'));
|
||||
Acc_Typ : constant Entity_Id :=
|
||||
Make_Defining_Identifier (Loc,
|
||||
Chars => New_Internal_Name ('A'));
|
||||
Alloc_Node : Node_Id;
|
||||
|
||||
begin
|
||||
Set_Ekind (Acc_Typ, E_Access_Type);
|
||||
|
||||
Set_Associated_Storage_Pool (Acc_Typ, RTE (RE_SS_Pool));
|
||||
|
||||
Alloc_Node :=
|
||||
Make_Allocator (Loc,
|
||||
Expression =>
|
||||
Make_Qualified_Expression (Loc,
|
||||
Subtype_Mark => New_Reference_To (Etype (Exp), Loc),
|
||||
Expression => Relocate_Node (Exp)));
|
||||
|
||||
Insert_List_Before_And_Analyze (N, New_List (
|
||||
Make_Full_Type_Declaration (Loc,
|
||||
Defining_Identifier => Acc_Typ,
|
||||
Type_Definition =>
|
||||
Make_Access_To_Object_Definition (Loc,
|
||||
Subtype_Indication =>
|
||||
New_Reference_To (Return_Type, Loc))),
|
||||
|
||||
Make_Object_Declaration (Loc,
|
||||
Defining_Identifier => Temp,
|
||||
Object_Definition => New_Reference_To (Acc_Typ, Loc),
|
||||
Expression => Alloc_Node)));
|
||||
|
||||
Rewrite (Exp,
|
||||
Make_Explicit_Dereference (Loc,
|
||||
Prefix => New_Reference_To (Temp, Loc)));
|
||||
|
||||
Analyze_And_Resolve (Exp, Return_Type);
|
||||
end;
|
||||
|
||||
-- Otherwise use the gigi mechanism to allocate result on the
|
||||
-- secondary stack.
|
||||
|
||||
else
|
||||
Set_Storage_Pool (N, RTE (RE_SS_Pool));
|
||||
|
||||
-- If we are generating code for the VM do not use
|
||||
-- SS_Allocate since everything is heap-allocated anyway.
|
||||
|
||||
if VM_Target = No_VM then
|
||||
Set_Procedure_To_Call (N, RTE (RE_SS_Allocate));
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Implement the rules of 6.5(8-10), which require a tag check in the
|
||||
-- case of a limited tagged return type, and tag reassignment for
|
||||
-- nonlimited tagged results. These actions are needed when the return
|
||||
-- type is a specific tagged type and the result expression is a
|
||||
-- conversion or a formal parameter, because in that case the tag of the
|
||||
-- expression might differ from the tag of the specific result type.
|
||||
|
||||
if Is_Tagged_Type (Utyp)
|
||||
and then not Is_Class_Wide_Type (Utyp)
|
||||
and then (Nkind (Exp) = N_Type_Conversion
|
||||
or else Nkind (Exp) = N_Unchecked_Type_Conversion
|
||||
or else (Is_Entity_Name (Exp)
|
||||
and then Ekind (Entity (Exp)) in Formal_Kind))
|
||||
then
|
||||
-- When the return type is limited, perform a check that the tag of
|
||||
-- the result is the same as the tag of the return type.
|
||||
|
||||
if Is_Limited_Type (Return_Type) then
|
||||
Insert_Action (Exp,
|
||||
Make_Raise_Constraint_Error (Loc,
|
||||
Condition =>
|
||||
Make_Op_Ne (Loc,
|
||||
Left_Opnd =>
|
||||
Make_Selected_Component (Loc,
|
||||
Prefix => Duplicate_Subexpr (Exp),
|
||||
Selector_Name =>
|
||||
New_Reference_To (First_Tag_Component (Utyp), Loc)),
|
||||
Right_Opnd =>
|
||||
Unchecked_Convert_To (RTE (RE_Tag),
|
||||
New_Reference_To
|
||||
(Node (First_Elmt
|
||||
(Access_Disp_Table (Base_Type (Utyp)))),
|
||||
Loc))),
|
||||
Reason => CE_Tag_Check_Failed));
|
||||
|
||||
-- If the result type is a specific nonlimited tagged type, then we
|
||||
-- have to ensure that the tag of the result is that of the result
|
||||
-- type. This is handled by making a copy of the expression in the
|
||||
-- case where it might have a different tag, namely when the
|
||||
-- expression is a conversion or a formal parameter. We create a new
|
||||
-- object of the result type and initialize it from the expression,
|
||||
-- which will implicitly force the tag to be set appropriately.
|
||||
|
||||
else
|
||||
Result_Id :=
|
||||
Make_Defining_Identifier (Loc, New_Internal_Name ('R'));
|
||||
Result_Exp := New_Reference_To (Result_Id, Loc);
|
||||
|
||||
Result_Obj :=
|
||||
Make_Object_Declaration (Loc,
|
||||
Defining_Identifier => Result_Id,
|
||||
Object_Definition => New_Reference_To (Return_Type, Loc),
|
||||
Constant_Present => True,
|
||||
Expression => Relocate_Node (Exp));
|
||||
|
||||
Set_Assignment_OK (Result_Obj);
|
||||
Insert_Action (Exp, Result_Obj);
|
||||
|
||||
Rewrite (Exp, Result_Exp);
|
||||
Analyze_And_Resolve (Exp, Return_Type);
|
||||
end if;
|
||||
|
||||
-- Ada 2005 (AI-344): If the result type is class-wide, then insert
|
||||
-- a check that the level of the return expression's underlying type
|
||||
-- is not deeper than the level of the master enclosing the function.
|
||||
-- Always generate the check when the type of the return expression
|
||||
-- is class-wide, when it's a type conversion, or when it's a formal
|
||||
-- parameter. Otherwise, suppress the check in the case where the
|
||||
-- return expression has a specific type whose level is known not to
|
||||
-- be statically deeper than the function's result type.
|
||||
|
||||
-- Note: accessibility check is skipped in the VM case, since there
|
||||
-- does not seem to be any practical way to implement this check.
|
||||
|
||||
elsif Ada_Version >= Ada_05
|
||||
and then VM_Target = No_VM
|
||||
and then Is_Class_Wide_Type (Return_Type)
|
||||
and then not Scope_Suppress (Accessibility_Check)
|
||||
and then
|
||||
(Is_Class_Wide_Type (Etype (Exp))
|
||||
or else Nkind (Exp) = N_Type_Conversion
|
||||
or else Nkind (Exp) = N_Unchecked_Type_Conversion
|
||||
or else (Is_Entity_Name (Exp)
|
||||
and then Ekind (Entity (Exp)) in Formal_Kind)
|
||||
or else Scope_Depth (Enclosing_Dynamic_Scope (Etype (Exp))) >
|
||||
Scope_Depth (Enclosing_Dynamic_Scope (Scope_Id)))
|
||||
then
|
||||
Insert_Action (Exp,
|
||||
Make_Raise_Program_Error (Loc,
|
||||
Condition =>
|
||||
Make_Op_Gt (Loc,
|
||||
Left_Opnd =>
|
||||
Build_Get_Access_Level (Loc,
|
||||
Make_Attribute_Reference (Loc,
|
||||
Prefix => Duplicate_Subexpr (Exp),
|
||||
Attribute_Name => Name_Tag)),
|
||||
Right_Opnd =>
|
||||
Make_Integer_Literal (Loc,
|
||||
Scope_Depth (Enclosing_Dynamic_Scope (Scope_Id)))),
|
||||
Reason => PE_Accessibility_Check_Failed));
|
||||
end if;
|
||||
when others =>
|
||||
raise Program_Error;
|
||||
end case;
|
||||
|
||||
exception
|
||||
when RE_Not_Available =>
|
||||
return;
|
||||
end Expand_N_Return_Statement;
|
||||
end Expand_N_Simple_Return_Statement;
|
||||
|
||||
--------------------------------
|
||||
-- Expand_Non_Function_Return --
|
||||
@ -3854,7 +3480,7 @@ package body Exp_Ch5 is
|
||||
Lab_Node : Node_Id;
|
||||
|
||||
begin
|
||||
-- If it is a return from procedures do no extra steps
|
||||
-- If it is a return from a procedure do no extra steps
|
||||
|
||||
if Kind = E_Procedure or else Kind = E_Generic_Procedure then
|
||||
return;
|
||||
@ -3864,7 +3490,7 @@ package body Exp_Ch5 is
|
||||
|
||||
elsif Kind = E_Return_Statement then
|
||||
Rewrite (N,
|
||||
Make_Return_Statement (Loc,
|
||||
Make_Simple_Return_Statement (Loc,
|
||||
Expression =>
|
||||
New_Occurrence_Of (First_Entity (Scope_Id), Loc)));
|
||||
Set_Comes_From_Extended_Return_Statement (N);
|
||||
@ -3938,36 +3564,6 @@ package body Exp_Ch5 is
|
||||
end if;
|
||||
end Expand_Non_Function_Return;
|
||||
|
||||
--------------------------
|
||||
-- Expand_Simple_Return --
|
||||
--------------------------
|
||||
|
||||
procedure Expand_Simple_Return (N : Node_Id) is
|
||||
begin
|
||||
-- Distinguish the function and non-function cases:
|
||||
|
||||
case Ekind (Return_Applies_To (Return_Statement_Entity (N))) is
|
||||
|
||||
when E_Function |
|
||||
E_Generic_Function =>
|
||||
Expand_Simple_Function_Return (N);
|
||||
|
||||
when E_Procedure |
|
||||
E_Generic_Procedure |
|
||||
E_Entry |
|
||||
E_Entry_Family |
|
||||
E_Return_Statement =>
|
||||
Expand_Non_Function_Return (N);
|
||||
|
||||
when others =>
|
||||
raise Program_Error;
|
||||
end case;
|
||||
|
||||
exception
|
||||
when RE_Not_Available =>
|
||||
return;
|
||||
end Expand_Simple_Return;
|
||||
|
||||
-----------------------------------
|
||||
-- Expand_Simple_Function_Return --
|
||||
-----------------------------------
|
||||
@ -4128,7 +3724,7 @@ package body Exp_Ch5 is
|
||||
-- stack on which we are going to put the result. Not only may this
|
||||
-- introduce secondary stack leaks but worse, if the reclamation is
|
||||
-- done too early, then the result we are returning may get
|
||||
-- clobbered. See example in 7417-003.
|
||||
-- clobbered.
|
||||
|
||||
declare
|
||||
S : Entity_Id;
|
||||
|
Loading…
Reference in New Issue
Block a user