aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/exp_ch5.adb
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/exp_ch5.adb')
-rw-r--r--gcc/ada/exp_ch5.adb991
1 files changed, 816 insertions, 175 deletions
diff --git a/gcc/ada/exp_ch5.adb b/gcc/ada/exp_ch5.adb
index 4cae2ee..8ac9662 100644
--- a/gcc/ada/exp_ch5.adb
+++ b/gcc/ada/exp_ch5.adb
@@ -6,7 +6,7 @@
-- --
-- B o d y --
-- --
--- Copyright (C) 1992-2020, Free Software Foundation, Inc. --
+-- Copyright (C) 1992-2021, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
@@ -23,43 +23,50 @@
-- --
------------------------------------------------------------------------------
-with Aspects; use Aspects;
-with Atree; use Atree;
-with Checks; use Checks;
-with Debug; use Debug;
-with Einfo; use Einfo;
-with Elists; use Elists;
-with Exp_Aggr; use Exp_Aggr;
-with Exp_Ch6; use Exp_Ch6;
-with Exp_Ch7; use Exp_Ch7;
-with Exp_Ch11; use Exp_Ch11;
-with Exp_Dbug; use Exp_Dbug;
-with Exp_Pakd; use Exp_Pakd;
-with Exp_Tss; use Exp_Tss;
-with Exp_Util; use Exp_Util;
-with Inline; use Inline;
-with Namet; use Namet;
-with Nlists; use Nlists;
-with Nmake; use Nmake;
-with Opt; use Opt;
-with Restrict; use Restrict;
-with Rident; use Rident;
-with Rtsfind; use Rtsfind;
-with Sinfo; use Sinfo;
-with Sem; use Sem;
-with Sem_Aux; use Sem_Aux;
-with Sem_Ch3; use Sem_Ch3;
-with Sem_Ch8; use Sem_Ch8;
-with Sem_Ch13; use Sem_Ch13;
-with Sem_Eval; use Sem_Eval;
-with Sem_Res; use Sem_Res;
-with Sem_Util; use Sem_Util;
-with Snames; use Snames;
-with Stand; use Stand;
-with Stringt; use Stringt;
-with Tbuild; use Tbuild;
-with Uintp; use Uintp;
-with Validsw; use Validsw;
+with Aspects; use Aspects;
+with Atree; use Atree;
+with Checks; use Checks;
+with Debug; use Debug;
+with Einfo; use Einfo;
+with Einfo.Entities; use Einfo.Entities;
+with Einfo.Utils; use Einfo.Utils;
+with Elists; use Elists;
+with Errout; use Errout;
+with Exp_Aggr; use Exp_Aggr;
+with Exp_Ch6; use Exp_Ch6;
+with Exp_Ch7; use Exp_Ch7;
+with Exp_Ch11; use Exp_Ch11;
+with Exp_Dbug; use Exp_Dbug;
+with Exp_Pakd; use Exp_Pakd;
+with Exp_Tss; use Exp_Tss;
+with Exp_Util; use Exp_Util;
+with Expander; use Expander;
+with Inline; use Inline;
+with Namet; use Namet;
+with Nlists; use Nlists;
+with Nmake; use Nmake;
+with Opt; use Opt;
+with Restrict; use Restrict;
+with Rident; use Rident;
+with Rtsfind; use Rtsfind;
+with Sinfo; use Sinfo;
+with Sinfo.Nodes; use Sinfo.Nodes;
+with Sinfo.Utils; use Sinfo.Utils;
+with Sem; use Sem;
+with Sem_Aux; use Sem_Aux;
+with Sem_Ch3; use Sem_Ch3;
+with Sem_Ch8; use Sem_Ch8;
+with Sem_Ch13; use Sem_Ch13;
+with Sem_Eval; use Sem_Eval;
+with Sem_Res; use Sem_Res;
+with Sem_Util; use Sem_Util;
+with Snames; use Snames;
+with Stand; use Stand;
+with Stringt; use Stringt;
+with Tbuild; use Tbuild;
+with Ttypes; use Ttypes;
+with Uintp; use Uintp;
+with Validsw; use Validsw;
package body Exp_Ch5 is
@@ -121,8 +128,16 @@ package body Exp_Ch5 is
R_Type : Entity_Id;
Rev : Boolean) return Node_Id;
-- Alternative to Expand_Assign_Array_Loop for packed bitfields. Generates
- -- a call to the System.Bitfields.Copy_Bitfield, which is more efficient
- -- than copying component-by-component.
+ -- a call to System.Bitfields.Copy_Bitfield, which is more efficient than
+ -- copying component-by-component.
+
+ function Expand_Assign_Array_Bitfield_Fast
+ (N : Node_Id;
+ Larray : Entity_Id;
+ Rarray : Entity_Id) return Node_Id;
+ -- Alternative to Expand_Assign_Array_Bitfield. Generates a call to
+ -- System.Bitfields.Fast_Copy_Bitfield, which is more efficient than
+ -- Copy_Bitfield, but only works in restricted situations.
function Expand_Assign_Array_Loop_Or_Bitfield
(N : Node_Id;
@@ -132,8 +147,8 @@ package body Exp_Ch5 is
R_Type : Entity_Id;
Ndim : Pos;
Rev : Boolean) return Node_Id;
- -- Calls either Expand_Assign_Array_Loop or Expand_Assign_Array_Bitfield as
- -- appropriate.
+ -- Calls either Expand_Assign_Array_Loop, Expand_Assign_Array_Bitfield, or
+ -- Expand_Assign_Array_Bitfield_Fast as appropriate.
procedure Expand_Assign_Record (N : Node_Id);
-- N is an assignment of an untagged record value. This routine handles
@@ -1434,6 +1449,139 @@ package body Exp_Ch5 is
R_Addr, R_Bit, L_Addr, L_Bit, Size));
end Expand_Assign_Array_Bitfield;
+ ---------------------------------------
+ -- Expand_Assign_Array_Bitfield_Fast --
+ ---------------------------------------
+
+ function Expand_Assign_Array_Bitfield_Fast
+ (N : Node_Id;
+ Larray : Entity_Id;
+ Rarray : Entity_Id) return Node_Id
+ is
+ pragma Assert (not Change_Of_Representation (N));
+ -- This won't work, for example, to copy a packed array to an unpacked
+ -- array.
+
+ -- For L (A .. B) := R (C .. D), we generate:
+ --
+ -- L := Fast_Copy_Bitfield (R, <offset of R(C)>, L, <offset of L(A)>,
+ -- L (A .. B)'Length * L'Component_Size);
+ --
+ -- with L and R suitably uncheckedly converted to/from Val_2.
+ -- The offsets are from the start of L and R.
+
+ Loc : constant Source_Ptr := Sloc (N);
+
+ L_Typ : constant Entity_Id := Etype (Larray);
+ R_Typ : constant Entity_Id := Etype (Rarray);
+ -- The original type of the arrays
+
+ L_Val : constant Node_Id :=
+ Unchecked_Convert_To (RTE (RE_Val_2), Larray);
+ R_Val : constant Node_Id :=
+ Unchecked_Convert_To (RTE (RE_Val_2), Rarray);
+ -- Converted values of left- and right-hand sides
+
+ L_Small : constant Boolean :=
+ Known_Static_RM_Size (L_Typ)
+ and then RM_Size (L_Typ) < Standard_Long_Long_Integer_Size;
+ R_Small : constant Boolean :=
+ Known_Static_RM_Size (R_Typ)
+ and then RM_Size (R_Typ) < Standard_Long_Long_Integer_Size;
+ -- Whether the above unchecked conversions need to be padded with zeros
+
+ C_Size : constant Uint := Component_Size (L_Typ);
+ pragma Assert (C_Size >= 1);
+ pragma Assert (C_Size = Component_Size (R_Typ));
+
+ Larray_Bounds : constant Range_Values :=
+ Get_Index_Bounds (First_Index (L_Typ));
+ L_Bounds : constant Range_Values :=
+ (if Nkind (Name (N)) = N_Slice
+ then Get_Index_Bounds (Discrete_Range (Name (N)))
+ else Larray_Bounds);
+ -- If the left-hand side is A (First..Last), Larray_Bounds is A'Range,
+ -- and L_Bounds is First..Last. If it's not a slice, we treat it like
+ -- a slice starting at A'First.
+
+ L_Bit : constant Node_Id :=
+ Make_Integer_Literal
+ (Loc, (L_Bounds.First - Larray_Bounds.First) * C_Size);
+
+ Rarray_Bounds : constant Range_Values :=
+ Get_Index_Bounds (First_Index (R_Typ));
+ R_Bounds : constant Range_Values :=
+ (if Nkind (Expression (N)) = N_Slice
+ then Get_Index_Bounds (Discrete_Range (Expression (N)))
+ else Rarray_Bounds);
+
+ R_Bit : constant Node_Id :=
+ Make_Integer_Literal
+ (Loc, (R_Bounds.First - Rarray_Bounds.First) * C_Size);
+
+ Size : constant Node_Id :=
+ Make_Op_Multiply (Loc,
+ Make_Attribute_Reference (Loc,
+ Prefix =>
+ Duplicate_Subexpr (Name (N), True),
+ Attribute_Name => Name_Length),
+ Make_Attribute_Reference (Loc,
+ Prefix =>
+ Duplicate_Subexpr (Larray, True),
+ Attribute_Name => Name_Component_Size));
+
+ L_Arg, R_Arg, Call : Node_Id;
+
+ begin
+ -- The semantics of unchecked conversion between bit-packed arrays that
+ -- are implemented as modular types and modular types is precisely that
+ -- of unchecked conversion between modular types. Therefore, if it needs
+ -- to be padded with zeros, the padding must be moved to the correct end
+ -- for memory order because System.Bitfield_Utils works in memory order.
+
+ if L_Small
+ and then (Bytes_Big_Endian xor Reverse_Storage_Order (L_Typ))
+ then
+ L_Arg := Make_Op_Shift_Left (Loc,
+ Left_Opnd => L_Val,
+ Right_Opnd => Make_Integer_Literal (Loc,
+ Standard_Long_Long_Integer_Size - RM_Size (L_Typ)));
+ else
+ L_Arg := L_Val;
+ end if;
+
+ if R_Small
+ and then (Bytes_Big_Endian xor Reverse_Storage_Order (R_Typ))
+ then
+ R_Arg := Make_Op_Shift_Left (Loc,
+ Left_Opnd => R_Val,
+ Right_Opnd => Make_Integer_Literal (Loc,
+ Standard_Long_Long_Integer_Size - RM_Size (R_Typ)));
+ else
+ R_Arg := R_Val;
+ end if;
+
+ Call := Make_Function_Call (Loc,
+ Name => New_Occurrence_Of (RTE (RE_Fast_Copy_Bitfield), Loc),
+ Parameter_Associations => New_List (
+ R_Arg, R_Bit, L_Arg, L_Bit, Size));
+
+ -- Conversely, the final unchecked conversion must take significant bits
+
+ if L_Small
+ and then (Bytes_Big_Endian xor Reverse_Storage_Order (L_Typ))
+ then
+ Call := Make_Op_Shift_Right (Loc,
+ Left_Opnd => Call,
+ Right_Opnd => Make_Integer_Literal (Loc,
+ Standard_Long_Long_Integer_Size - RM_Size (L_Typ)));
+ end if;
+
+ return Make_Assignment_Statement (Loc,
+ Name => Duplicate_Subexpr (Larray, True),
+ Expression => Unchecked_Convert_To (L_Typ, Call));
+ end Expand_Assign_Array_Bitfield_Fast;
+
------------------------------------------
-- Expand_Assign_Array_Loop_Or_Bitfield --
------------------------------------------
@@ -1447,37 +1595,42 @@ package body Exp_Ch5 is
Ndim : Pos;
Rev : Boolean) return Node_Id
is
+
+ L : constant Node_Id := Name (N);
+ R : constant Node_Id := Expression (N);
+ -- Left- and right-hand sides of the assignment statement
+
Slices : constant Boolean :=
- Nkind (Name (N)) = N_Slice or else Nkind (Expression (N)) = N_Slice;
+ Nkind (L) = N_Slice or else Nkind (R) = N_Slice;
L_Prefix_Comp : constant Boolean :=
-- True if the left-hand side is a slice of a component or slice
- Nkind (Name (N)) = N_Slice
- and then Nkind (Prefix (Name (N))) in
+ Nkind (L) = N_Slice
+ and then Nkind (Prefix (L)) in
N_Selected_Component | N_Indexed_Component | N_Slice;
R_Prefix_Comp : constant Boolean :=
-- Likewise for the right-hand side
- Nkind (Expression (N)) = N_Slice
- and then Nkind (Prefix (Expression (N))) in
+ Nkind (R) = N_Slice
+ and then Nkind (Prefix (R)) in
N_Selected_Component | N_Indexed_Component | N_Slice;
begin
- -- Determine whether Copy_Bitfield is appropriate (will work, and will
- -- be more efficient than component-by-component copy). Copy_Bitfield
- -- doesn't work for reversed storage orders. It is efficient for slices
- -- of bit-packed arrays. Copy_Bitfield can read and write bits that are
- -- not part of the objects being copied, so we don't want to use it if
- -- there are volatile or independent components. If the Prefix of the
- -- slice is a component or slice, then it might be a part of an object
- -- with some other volatile or independent components, so we disable the
- -- optimization in that case as well. We could complicate this code by
- -- actually looking for such volatile and independent components.
+ -- Determine whether Copy_Bitfield or Fast_Copy_Bitfield is appropriate
+ -- (will work, and will be more efficient than component-by-component
+ -- copy). Copy_Bitfield doesn't work for reversed storage orders. It is
+ -- efficient for slices of bit-packed arrays. Copy_Bitfield can read and
+ -- write bits that are not part of the objects being copied, so we don't
+ -- want to use it if there are volatile or independent components. If
+ -- the Prefix of the slice is a component or slice, then it might be a
+ -- part of an object with some other volatile or independent components,
+ -- so we disable the optimization in that case as well. We could
+ -- complicate this code by actually looking for such volatile and
+ -- independent components.
if Is_Bit_Packed_Array (L_Type)
and then Is_Bit_Packed_Array (R_Type)
and then not Reverse_Storage_Order (L_Type)
and then not Reverse_Storage_Order (R_Type)
and then Ndim = 1
- and then not Rev
and then Slices
and then not Has_Volatile_Component (L_Type)
and then not Has_Volatile_Component (R_Type)
@@ -1485,14 +1638,88 @@ package body Exp_Ch5 is
and then not Has_Independent_Components (R_Type)
and then not L_Prefix_Comp
and then not R_Prefix_Comp
- and then RTE_Available (RE_Copy_Bitfield)
then
- return Expand_Assign_Array_Bitfield
- (N, Larray, Rarray, L_Type, R_Type, Rev);
- else
- return Expand_Assign_Array_Loop
- (N, Larray, Rarray, L_Type, R_Type, Ndim, Rev);
+ -- Here if Copy_Bitfield can work (except for the Rev test below).
+ -- Determine whether to call Fast_Copy_Bitfield instead. If we
+ -- are assigning slices, and all the relevant bounds are known at
+ -- compile time, and the maximum object size is no greater than
+ -- System.Bitfields.Val_Bits (i.e. Long_Long_Integer'Size / 2), and
+ -- we don't have enumeration representation clauses, we can use
+ -- Fast_Copy_Bitfield. The max size test is to ensure that the slices
+ -- cannot overlap boundaries not supported by Fast_Copy_Bitfield.
+
+ pragma Assert (Known_Component_Size (Base_Type (L_Type)));
+ pragma Assert (Known_Component_Size (Base_Type (R_Type)));
+
+ -- Note that L_Type and R_Type do not necessarily have the same base
+ -- type, because of array type conversions. Hence the need to check
+ -- various properties of both.
+
+ if Compile_Time_Known_Bounds (Base_Type (L_Type))
+ and then Compile_Time_Known_Bounds (Base_Type (R_Type))
+ then
+ declare
+ Left_Base_Index : constant Entity_Id :=
+ First_Index (Base_Type (L_Type));
+ Left_Base_Range : constant Range_Values :=
+ Get_Index_Bounds (Left_Base_Index);
+
+ Right_Base_Index : constant Entity_Id :=
+ First_Index (Base_Type (R_Type));
+ Right_Base_Range : constant Range_Values :=
+ Get_Index_Bounds (Right_Base_Index);
+
+ Known_Left_Slice_Low : constant Boolean :=
+ (if Nkind (L) = N_Slice
+ then Compile_Time_Known_Value
+ (Get_Index_Bounds (Discrete_Range (L)).First));
+ Known_Right_Slice_Low : constant Boolean :=
+ (if Nkind (R) = N_Slice
+ then Compile_Time_Known_Value
+ (Get_Index_Bounds (Discrete_Range (R)).Last));
+
+ Val_Bits : constant Pos := Standard_Long_Long_Integer_Size / 2;
+
+ begin
+ if Left_Base_Range.Last - Left_Base_Range.First < Val_Bits
+ and then Right_Base_Range.Last - Right_Base_Range.First <
+ Val_Bits
+ and then Known_Esize (L_Type)
+ and then Known_Esize (R_Type)
+ and then Known_Left_Slice_Low
+ and then Known_Right_Slice_Low
+ and then Compile_Time_Known_Value
+ (Get_Index_Bounds (First_Index (Etype (Larray))).First)
+ and then Compile_Time_Known_Value
+ (Get_Index_Bounds (First_Index (Etype (Rarray))).First)
+ and then
+ not (Is_Enumeration_Type (Etype (Left_Base_Index))
+ and then Has_Enumeration_Rep_Clause
+ (Etype (Left_Base_Index)))
+ and then RTE_Available (RE_Fast_Copy_Bitfield)
+ then
+ pragma Assert (Esize (L_Type) /= 0);
+ pragma Assert (Esize (R_Type) /= 0);
+
+ return Expand_Assign_Array_Bitfield_Fast (N, Larray, Rarray);
+ end if;
+ end;
+ end if;
+
+ -- Fast_Copy_Bitfield can work if Rev is True, because the data is
+ -- passed and returned by copy. Copy_Bitfield cannot.
+
+ if not Rev and then RTE_Available (RE_Copy_Bitfield) then
+ return Expand_Assign_Array_Bitfield
+ (N, Larray, Rarray, L_Type, R_Type, Rev);
+ end if;
end if;
+
+ -- Here if we did not return above, with Fast_Copy_Bitfield or
+ -- Copy_Bitfield.
+
+ return Expand_Assign_Array_Loop
+ (N, Larray, Rarray, L_Type, R_Type, Ndim, Rev);
end Expand_Assign_Array_Loop_Or_Bitfield;
--------------------------
@@ -2544,7 +2771,9 @@ package body Exp_Ch5 is
(Entity (Lhs)), Loc),
Expression =>
Accessibility_Level
- (Rhs, Dynamic_Level));
+ (Expr => Rhs,
+ Level => Dynamic_Level,
+ Allow_Alt_Model => False));
begin
if not Accessibility_Checks_Suppressed (Entity (Lhs)) then
@@ -3027,7 +3256,444 @@ package body Exp_Ch5 is
Choice : Node_Id;
Chlist : List_Id;
+ function Expand_General_Case_Statement return Node_Id;
+ -- Expand a case statement whose selecting expression is not discrete
+
+ -----------------------------------
+ -- Expand_General_Case_Statement --
+ -----------------------------------
+
+ function Expand_General_Case_Statement return Node_Id is
+ -- expand into a block statement
+
+ Selector : constant Entity_Id :=
+ Make_Temporary (Loc, 'J');
+
+ function Selector_Subtype_Mark return Node_Id is
+ (New_Occurrence_Of (Etype (Expr), Loc));
+
+ Renamed_Name : constant Node_Id :=
+ (if Is_Name_Reference (Expr)
+ then Expr
+ else Make_Qualified_Expression (Loc,
+ Subtype_Mark => Selector_Subtype_Mark,
+ Expression => Expr));
+
+ Selector_Decl : constant Node_Id :=
+ Make_Object_Renaming_Declaration (Loc,
+ Defining_Identifier => Selector,
+ Subtype_Mark => Selector_Subtype_Mark,
+ Name => Renamed_Name);
+
+ First_Alt : constant Node_Id := First (Alternatives (N));
+
+ function Choice_Index_Decl_If_Needed return Node_Id;
+ -- If we are going to need a choice index object (that is, if
+ -- Multidefined_Bindings is true for at least one of the case
+ -- alternatives), then create and return that object's declaration.
+ -- Otherwise, return Empty; no need for a decl in that case because
+ -- it would never be referenced.
+
+ ---------------------------------
+ -- Choice_Index_Decl_If_Needed --
+ ---------------------------------
+
+ function Choice_Index_Decl_If_Needed return Node_Id is
+ Alt : Node_Id := First_Alt;
+ begin
+ while Present (Alt) loop
+ if Multidefined_Bindings (Alt) then
+ return Make_Object_Declaration
+ (Sloc => Loc,
+ Defining_Identifier =>
+ Make_Temporary (Loc, 'K'),
+ Object_Definition =>
+ New_Occurrence_Of (Standard_Positive, Loc));
+ end if;
+
+ Next (Alt);
+ end loop;
+ return Empty; -- decl not needed
+ end Choice_Index_Decl_If_Needed;
+
+ Choice_Index_Decl : constant Node_Id := Choice_Index_Decl_If_Needed;
+
+ function Pattern_Match
+ (Pattern : Node_Id;
+ Object : Node_Id;
+ Choice_Index : Natural;
+ Alt : Node_Id;
+ Suppress_Choice_Index_Update : Boolean := False) return Node_Id;
+ -- Returns a Boolean-valued expression indicating a pattern match
+ -- for a given pattern and object. If Choice_Index is nonzero,
+ -- then Choice_Index is assigned to Choice_Index_Decl (unless
+ -- Suppress_Choice_Index_Update is specified, which should only
+ -- be the case for a recursive call where the caller has already
+ -- taken care of the update). Pattern occurs as a choice (or as a
+ -- subexpression of a choice) of the case statement alternative Alt.
+
+ function Top_Level_Pattern_Match_Condition
+ (Alt : Node_Id) return Node_Id;
+ -- Returns a Boolean-valued expression indicating a pattern match
+ -- for the given alternative's list of choices.
+
+ -------------------
+ -- Pattern_Match --
+ -------------------
+
+ function Pattern_Match
+ (Pattern : Node_Id;
+ Object : Node_Id;
+ Choice_Index : Natural;
+ Alt : Node_Id;
+ Suppress_Choice_Index_Update : Boolean := False) return Node_Id
+ is
+ function Update_Choice_Index return Node_Id is (
+ Make_Assignment_Statement (Loc,
+ Name =>
+ New_Occurrence_Of
+ (Defining_Identifier (Choice_Index_Decl), Loc),
+ Expression => Make_Integer_Literal (Loc, Pos (Choice_Index))));
+
+ function PM
+ (Pattern : Node_Id;
+ Object : Node_Id;
+ Choice_Index : Natural := Pattern_Match.Choice_Index;
+ Alt : Node_Id := Pattern_Match.Alt;
+ Suppress_Choice_Index_Update : Boolean :=
+ Pattern_Match.Suppress_Choice_Index_Update) return Node_Id
+ renames Pattern_Match;
+ -- convenient rename for recursive calls
+
+ begin
+ if Choice_Index /= 0 and not Suppress_Choice_Index_Update then
+ pragma Assert (Present (Choice_Index_Decl));
+
+ -- Add Choice_Index update as a side effect of evaluating
+ -- this condition and try again, this time suppressing
+ -- Choice_Index update.
+
+ return Make_Expression_With_Actions (Loc,
+ Actions => New_List (Update_Choice_Index),
+ Expression =>
+ PM (Pattern, Object,
+ Suppress_Choice_Index_Update => True));
+ end if;
+
+ if Nkind (Pattern) in N_Has_Etype
+ and then Is_Discrete_Type (Etype (Pattern))
+ and then Compile_Time_Known_Value (Pattern)
+ then
+ declare
+ Val : Node_Id;
+ begin
+ if Is_Enumeration_Type (Etype (Pattern)) then
+ Val := Get_Enum_Lit_From_Pos
+ (Etype (Pattern), Expr_Value (Pattern), Loc);
+ else
+ Val := Make_Integer_Literal (Loc, Expr_Value (Pattern));
+ end if;
+ return Make_Op_Eq (Loc, Object, Val);
+ end;
+ end if;
+
+ case Nkind (Pattern) is
+ when N_Aggregate =>
+ return Result : Node_Id :=
+ New_Occurrence_Of (Standard_True, Loc)
+ do
+ if Is_Array_Type (Etype (Pattern)) then
+ -- Calling Error_Msg_N during expansion is usually a
+ -- mistake but is ok for an "unimplemented" message.
+ Error_Msg_N
+ ("array-valued case choices unimplemented",
+ Pattern);
+ return;
+ end if;
+
+ -- positional notation should have been normalized
+ pragma Assert (No (Expressions (Pattern)));
+
+ declare
+ Component_Assoc : Node_Id
+ := First (Component_Associations (Pattern));
+ Choice : Node_Id;
+
+ function Subobject return Node_Id is
+ (Make_Selected_Component (Loc,
+ Prefix => New_Copy_Tree (Object),
+ Selector_Name => New_Occurrence_Of
+ (Entity (Choice), Loc)));
+ begin
+ while Present (Component_Assoc) loop
+ Choice := First (Choices (Component_Assoc));
+ while Present (Choice) loop
+ pragma Assert
+ (Is_Entity_Name (Choice)
+ and then Ekind (Entity (Choice))
+ in E_Discriminant | E_Component);
+
+ if Box_Present (Component_Assoc) then
+ -- Box matches anything
+
+ pragma Assert
+ (No (Expression (Component_Assoc)));
+ else
+ Result := Make_And_Then (Loc,
+ Left_Opnd => Result,
+ Right_Opnd =>
+ PM (Pattern =>
+ Expression
+ (Component_Assoc),
+ Object => Subobject));
+ end if;
+
+ -- If this component association defines
+ -- (in the case where the pattern matches)
+ -- the value of a binding object, then
+ -- prepend to the statement list for this
+ -- alternative an assignment to the binding
+ -- object. This assignment will be conditional
+ -- if there is more than one choice.
+
+ if Binding_Chars (Component_Assoc) /= No_Name
+ then
+ declare
+ Decl_Chars : constant Name_Id :=
+ Binding_Chars (Component_Assoc);
+
+ Block_Stmt : constant Node_Id :=
+ First (Statements (Alt));
+ pragma Assert
+ (Nkind (Block_Stmt) = N_Block_Statement);
+ pragma Assert (No (Next (Block_Stmt)));
+ Decl : Node_Id
+ := First (Declarations (Block_Stmt));
+ Def_Id : Node_Id := Empty;
+
+ Assignment_Stmt : Node_Id;
+ Condition : Node_Id;
+ Prepended_Stmt : Node_Id;
+ begin
+ -- find the variable to be modified
+ while No (Def_Id) or else
+ Chars (Def_Id) /= Decl_Chars
+ loop
+ Def_Id := Defining_Identifier (Decl);
+ Next (Decl);
+ end loop;
+
+ Assignment_Stmt :=
+ Make_Assignment_Statement (Loc,
+ Name => New_Occurrence_Of
+ (Def_Id, Loc),
+ Expression => Subobject);
+
+ -- conditional if multiple choices
+
+ if Present (Choice_Index_Decl) then
+ Condition :=
+ Make_Op_Eq (Loc,
+ New_Occurrence_Of
+ (Defining_Identifier
+ (Choice_Index_Decl), Loc),
+ Make_Integer_Literal
+ (Loc, Int (Choice_Index)));
+
+ Prepended_Stmt :=
+ Make_If_Statement (Loc,
+ Condition => Condition,
+ Then_Statements =>
+ New_List (Assignment_Stmt));
+ else
+ -- assignment is unconditional
+ Prepended_Stmt := Assignment_Stmt;
+ end if;
+
+ declare
+ HSS : constant Node_Id :=
+ Handled_Statement_Sequence
+ (Block_Stmt);
+ begin
+ Prepend (Prepended_Stmt,
+ Statements (HSS));
+
+ Set_Analyzed (Block_Stmt, False);
+ Set_Analyzed (HSS, False);
+ end;
+ end;
+ end if;
+
+ Next (Choice);
+ end loop;
+
+ Next (Component_Assoc);
+ end loop;
+ end;
+ end return;
+
+ when N_Qualified_Expression =>
+ -- Make a copy for one of the two uses of Object; the choice
+ -- of where to use the original and where to use the copy
+ -- is arbitrary.
+
+ return Make_And_Then (Loc,
+ Left_Opnd => Make_In (Loc,
+ Left_Opnd => New_Copy_Tree (Object),
+ Right_Opnd => New_Copy_Tree (Subtype_Mark (Pattern))),
+ Right_Opnd =>
+ PM (Pattern => Expression (Pattern),
+ Object => Object));
+
+ when N_Identifier | N_Expanded_Name =>
+ if Is_Type (Entity (Pattern)) then
+ return Make_In (Loc,
+ Left_Opnd => Object,
+ Right_Opnd => New_Occurrence_Of
+ (Entity (Pattern), Loc));
+ end if;
+
+ when N_Others_Choice =>
+ return New_Occurrence_Of (Standard_True, Loc);
+
+ when N_Type_Conversion =>
+ -- aggregate expansion sometimes introduces conversions
+ if not Comes_From_Source (Pattern)
+ and then Base_Type (Etype (Pattern))
+ = Base_Type (Etype (Expression (Pattern)))
+ then
+ return PM (Expression (Pattern), Object);
+ end if;
+
+ when others =>
+ null;
+ end case;
+
+ -- Avoid cascading errors
+ pragma Assert (Serious_Errors_Detected > 0);
+ return New_Occurrence_Of (Standard_True, Loc);
+ end Pattern_Match;
+
+ ---------------------------------------
+ -- Top_Level_Pattern_Match_Condition --
+ ---------------------------------------
+
+ function Top_Level_Pattern_Match_Condition
+ (Alt : Node_Id) return Node_Id
+ is
+ Top_Level_Object : constant Node_Id :=
+ New_Occurrence_Of (Selector, Loc);
+
+ Choices : constant List_Id := Discrete_Choices (Alt);
+
+ First_Choice : constant Node_Id := First (Choices);
+ Subsequent : Node_Id := Next (First_Choice);
+
+ Choice_Index : Natural := 0;
+ begin
+ if Multidefined_Bindings (Alt) then
+ Choice_Index := 1;
+ end if;
+
+ return Result : Node_Id :=
+ Pattern_Match (Pattern => First_Choice,
+ Object => Top_Level_Object,
+ Choice_Index => Choice_Index,
+ Alt => Alt)
+ do
+ while Present (Subsequent) loop
+ if Choice_Index /= 0 then
+ Choice_Index := Choice_Index + 1;
+ end if;
+
+ Result := Make_Or_Else (Loc,
+ Left_Opnd => Result,
+ Right_Opnd => Pattern_Match
+ (Pattern => Subsequent,
+ Object => Top_Level_Object,
+ Choice_Index => Choice_Index,
+ Alt => Alt));
+ Subsequent := Next (Subsequent);
+ end loop;
+ end return;
+ end Top_Level_Pattern_Match_Condition;
+
+ function Elsif_Parts return List_Id;
+ -- Process subsequent alternatives
+
+ -----------------
+ -- Elsif_Parts --
+ -----------------
+
+ function Elsif_Parts return List_Id is
+ Alt : Node_Id := First_Alt;
+ Result : constant List_Id := New_List;
+ begin
+ loop
+ Alt := Next (Alt);
+ exit when No (Alt);
+
+ Append (Make_Elsif_Part (Loc,
+ Condition => Top_Level_Pattern_Match_Condition (Alt),
+ Then_Statements => Statements (Alt)),
+ Result);
+ end loop;
+ return Result;
+ end Elsif_Parts;
+
+ function Else_Statements return List_Id;
+ -- Returns a "raise Constraint_Error" statement if
+ -- exception propagate is permitted and No_List otherwise.
+
+ ---------------------
+ -- Else_Statements --
+ ---------------------
+
+ function Else_Statements return List_Id is
+ begin
+ if Restriction_Active (No_Exception_Propagation) then
+ return No_List;
+ else
+ return New_List (Make_Raise_Constraint_Error (Loc,
+ Reason => CE_Invalid_Data));
+ end if;
+ end Else_Statements;
+
+ -- Local constants
+
+ If_Stmt : constant Node_Id :=
+ Make_If_Statement (Loc,
+ Condition => Top_Level_Pattern_Match_Condition (First_Alt),
+ Then_Statements => Statements (First_Alt),
+ Elsif_Parts => Elsif_Parts,
+ Else_Statements => Else_Statements);
+
+ Declarations : constant List_Id := New_List (Selector_Decl);
+
+ -- Start of processing for Expand_General_Case_Statment
+
+ begin
+ if Present (Choice_Index_Decl) then
+ Append_To (Declarations, Choice_Index_Decl);
+ end if;
+
+ return Make_Block_Statement (Loc,
+ Declarations => Declarations,
+ Handled_Statement_Sequence =>
+ Make_Handled_Sequence_Of_Statements (Loc,
+ Statements => New_List (If_Stmt)));
+ end Expand_General_Case_Statement;
+
+ -- Start of processing for Expand_N_Case_Statement
+
begin
+ if Extensions_Allowed and then not Is_Discrete_Type (Etype (Expr)) then
+ Rewrite (N, Expand_General_Case_Statement);
+ Analyze (N);
+ Expand (N);
+ return;
+ end if;
+
-- Check for the situation where we know at compile time which branch
-- will be taken.
@@ -3403,7 +4069,7 @@ package body Exp_Ch5 is
Analyze (Init_Decl);
Init_Name := Defining_Identifier (Init_Decl);
- Set_Ekind (Init_Name, E_Loop_Parameter);
+ Mutate_Ekind (Init_Name, E_Loop_Parameter);
-- The cursor was marked as a loop parameter to prevent user assignments
-- to it, however this renders the advancement step illegal as it is not
@@ -3440,7 +4106,6 @@ package body Exp_Ch5 is
Make_Defining_Identifier (Loc,
Chars => New_External_Name (Chars (Element), 'C'));
Elmt_Decl : Node_Id;
- Elmt_Ref : Node_Id;
Element_Op : constant Entity_Id :=
Get_Iterable_Type_Primitive (Container_Typ, Name_Element);
@@ -3451,19 +4116,10 @@ package body Exp_Ch5 is
begin
-- For an element iterator, the Element aspect must be present,
- -- (this is checked during analysis) and the expansion takes the form:
+ -- (this is checked during analysis).
- -- Cursor : Cursor_Type := First (Container);
- -- Elmt : Element_Type;
- -- while Has_Element (Cursor, Container) loop
- -- Elmt := Element (Container, Cursor);
- -- <original loop statements>
- -- Cursor := Next (Container, Cursor);
- -- end loop;
-
- -- However this expansion is not legal if the element is indefinite.
- -- In that case we create a block to hold a variable declaration
- -- initialized with a call to Element, and generate:
+ -- We create a block to hold a variable declaration initialized with
+ -- a call to Element, and generate:
-- Cursor : Cursor_Type := First (Container);
-- while Has_Element (Cursor, Container) loop
@@ -3479,7 +4135,7 @@ package body Exp_Ch5 is
(N, Container, Cursor, Init, Advance, New_Loop);
Append_To (Stats, Advance);
- Set_Ekind (Cursor, E_Variable);
+ Mutate_Ekind (Cursor, E_Variable);
Insert_Action (N, Init);
-- The loop parameter is declared by an object declaration, but within
@@ -3495,48 +4151,20 @@ package body Exp_Ch5 is
Defining_Identifier => Element,
Object_Definition => New_Occurrence_Of (Etype (Element_Op), Loc));
- if not Is_Constrained (Etype (Element_Op)) then
- Set_Expression (Elmt_Decl,
- Make_Function_Call (Loc,
- Name => New_Occurrence_Of (Element_Op, Loc),
- Parameter_Associations => New_List (
- Convert_To_Iterable_Type (Container, Loc),
- New_Occurrence_Of (Cursor, Loc))));
-
- Set_Statements (New_Loop,
- New_List
- (Make_Block_Statement (Loc,
- Declarations => New_List (Elmt_Decl),
- Handled_Statement_Sequence =>
- Make_Handled_Sequence_Of_Statements (Loc,
- Statements => Stats))));
-
- else
- Elmt_Ref :=
- Make_Assignment_Statement (Loc,
- Name => New_Occurrence_Of (Element, Loc),
- Expression =>
- Make_Function_Call (Loc,
- Name => New_Occurrence_Of (Element_Op, Loc),
- Parameter_Associations => New_List (
- Convert_To_Iterable_Type (Container, Loc),
- New_Occurrence_Of (Cursor, Loc))));
-
- Prepend (Elmt_Ref, Stats);
-
- -- The element is assignable in the expanded code
-
- Set_Assignment_OK (Name (Elmt_Ref));
-
- -- The loop is rewritten as a block, to hold the element declaration
-
- New_Loop :=
- Make_Block_Statement (Loc,
- Declarations => New_List (Elmt_Decl),
+ Set_Expression (Elmt_Decl,
+ Make_Function_Call (Loc,
+ Name => New_Occurrence_Of (Element_Op, Loc),
+ Parameter_Associations => New_List (
+ Convert_To_Iterable_Type (Container, Loc),
+ New_Occurrence_Of (Cursor, Loc))));
+
+ Set_Statements (New_Loop,
+ New_List
+ (Make_Block_Statement (Loc,
+ Declarations => New_List (Elmt_Decl),
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
- Statements => New_List (New_Loop)));
- end if;
+ Statements => Stats))));
-- The element is only modified in expanded code, so it appears as
-- unassigned to the warning machinery. We must suppress this spurious
@@ -3548,12 +4176,29 @@ package body Exp_Ch5 is
Analyze (N);
end Expand_Formal_Container_Element_Loop;
+ ----------------------------------
+ -- Expand_N_Goto_When_Statement --
+ ----------------------------------
+
+ procedure Expand_N_Goto_When_Statement (N : Node_Id) is
+ Loc : constant Source_Ptr := Sloc (N);
+ begin
+ Rewrite (N,
+ Make_If_Statement (Loc,
+ Condition => Condition (N),
+ Then_Statements => New_List (
+ Make_Goto_Statement (Loc,
+ Name => Name (N)))));
+
+ Analyze (N);
+ end Expand_N_Goto_When_Statement;
+
---------------------------
-- Expand_N_If_Statement --
---------------------------
-- First we deal with the case of C and Fortran convention boolean values,
- -- with zero/non-zero semantics.
+ -- with zero/nonzero semantics.
-- Second, we deal with the obvious rewriting for the cases where the
-- condition of the IF is known at compile time to be True or False.
@@ -3788,62 +4433,58 @@ package body Exp_Ch5 is
-- return not (expression);
- -- Only do these optimizations if we are at least at -O1 level and
- -- do not do them if control flow optimizations are suppressed.
+ -- Do these optimizations only for internally generated code and only
+ -- when -fpreserve-control-flow isn't set, to preserve the original
+ -- source control flow.
- if Optimization_Level > 0
+ if not Comes_From_Source (N)
and then not Opt.Suppress_Control_Flow_Optimizations
+ and then Nkind (N) = N_If_Statement
+ and then No (Elsif_Parts (N))
+ and then Present (Else_Statements (N))
+ and then List_Length (Then_Statements (N)) = 1
+ and then List_Length (Else_Statements (N)) = 1
then
- if Nkind (N) = N_If_Statement
- and then No (Elsif_Parts (N))
- and then Present (Else_Statements (N))
- and then List_Length (Then_Statements (N)) = 1
- and then List_Length (Else_Statements (N)) = 1
- then
- declare
- Then_Stm : constant Node_Id := First (Then_Statements (N));
- Else_Stm : constant Node_Id := First (Else_Statements (N));
+ declare
+ Then_Stm : constant Node_Id := First (Then_Statements (N));
+ Else_Stm : constant Node_Id := First (Else_Statements (N));
- begin
- if Nkind (Then_Stm) = N_Simple_Return_Statement
+ Then_Expr : Node_Id;
+ Else_Expr : Node_Id;
+
+ begin
+ if Nkind (Then_Stm) = N_Simple_Return_Statement
+ and then
+ Nkind (Else_Stm) = N_Simple_Return_Statement
+ then
+ Then_Expr := Expression (Then_Stm);
+ Else_Expr := Expression (Else_Stm);
+
+ if Nkind (Then_Expr) in N_Expanded_Name | N_Identifier
and then
- Nkind (Else_Stm) = N_Simple_Return_Statement
+ Nkind (Else_Expr) in N_Expanded_Name | N_Identifier
then
- declare
- Then_Expr : constant Node_Id := Expression (Then_Stm);
- Else_Expr : constant Node_Id := Expression (Else_Stm);
+ if Entity (Then_Expr) = Standard_True
+ and then Entity (Else_Expr) = Standard_False
+ then
+ Rewrite (N,
+ Make_Simple_Return_Statement (Loc,
+ Expression => Relocate_Node (Condition (N))));
+ Analyze (N);
- begin
- if Nkind (Then_Expr) in N_Expanded_Name | N_Identifier
- and then
- Nkind (Else_Expr) in N_Expanded_Name | N_Identifier
- then
- if Entity (Then_Expr) = Standard_True
- and then Entity (Else_Expr) = Standard_False
- then
- Rewrite (N,
- Make_Simple_Return_Statement (Loc,
- Expression => Relocate_Node (Condition (N))));
- Analyze (N);
- return;
-
- elsif Entity (Then_Expr) = Standard_False
- and then Entity (Else_Expr) = Standard_True
- then
- Rewrite (N,
- Make_Simple_Return_Statement (Loc,
- Expression =>
- Make_Op_Not (Loc,
- Right_Opnd =>
- Relocate_Node (Condition (N)))));
- Analyze (N);
- return;
- end if;
- end if;
- end;
+ elsif Entity (Then_Expr) = Standard_False
+ and then Entity (Else_Expr) = Standard_True
+ then
+ Rewrite (N,
+ Make_Simple_Return_Statement (Loc,
+ Expression =>
+ Make_Op_Not (Loc,
+ Right_Opnd => Relocate_Node (Condition (N)))));
+ Analyze (N);
+ end if;
end if;
- end;
- end if;
+ end if;
+ end;
end if;
end Expand_N_If_Statement;
@@ -3900,7 +4541,7 @@ package body Exp_Ch5 is
begin
if Present (Iterator_Filter (I_Spec)) then
- pragma Assert (Ada_Version >= Ada_2020);
+ pragma Assert (Ada_Version >= Ada_2022);
Stats := New_List (Make_If_Statement (Loc,
Condition => Iterator_Filter (I_Spec),
Then_Statements => Stats));
@@ -4201,7 +4842,7 @@ package body Exp_Ch5 is
begin
if Present (Iterator_Filter (I_Spec)) then
- pragma Assert (Ada_Version >= Ada_2020);
+ pragma Assert (Ada_Version >= Ada_2022);
Stats := New_List (Make_If_Statement (Loc,
Condition => Iterator_Filter (I_Spec),
Then_Statements => Stats));
@@ -4484,7 +5125,7 @@ package body Exp_Ch5 is
(Container_Typ, Aspect_Variable_Indexing))
or else not Is_Variable (Original_Node (Container))
then
- Set_Ekind (Id, E_Constant);
+ Mutate_Ekind (Id, E_Constant);
end if;
Prepend_To (Stats, Decl);
@@ -4620,7 +5261,7 @@ package body Exp_Ch5 is
Set_Assignment_OK (Cursor_Decl);
Insert_Action (N, Cursor_Decl);
- Set_Ekind (Cursor, Id_Kind);
+ Mutate_Ekind (Cursor, Id_Kind);
end;
-- If the range of iteration is given by a function call that returns
@@ -4701,7 +5342,7 @@ package body Exp_Ch5 is
end if;
if Present (Iterator_Filter (LPS)) then
- pragma Assert (Ada_Version >= Ada_2020);
+ pragma Assert (Ada_Version >= Ada_2022);
Set_Statements (N,
New_List (Make_If_Statement (Loc,
Condition => Iterator_Filter (LPS),
@@ -5081,7 +5722,7 @@ package body Exp_Ch5 is
-- identifier, since there may be references in the loop body.
Set_Analyzed (Loop_Id, False);
- Set_Ekind (Loop_Id, E_Variable);
+ Mutate_Ekind (Loop_Id, E_Variable);
-- In most loops the loop variable is assigned in various
-- alternatives in the body. However, in the rare case when