aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/sem_case.adb
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/sem_case.adb')
-rw-r--r--gcc/ada/sem_case.adb1751
1 files changed, 1706 insertions, 45 deletions
diff --git a/gcc/ada/sem_case.adb b/gcc/ada/sem_case.adb
index 6cda6a9..7d08da5 100644
--- a/gcc/ada/sem_case.adb
+++ b/gcc/ada/sem_case.adb
@@ -6,7 +6,7 @@
-- --
-- B o d y --
-- --
--- Copyright (C) 1996-2020, Free Software Foundation, Inc. --
+-- Copyright (C) 1996-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,28 +23,35 @@
-- --
------------------------------------------------------------------------------
-with Atree; use Atree;
-with Einfo; use Einfo;
-with Errout; use Errout;
-with Namet; use Namet;
-with Nlists; use Nlists;
-with Nmake; use Nmake;
-with Opt; use Opt;
-with Sem; use Sem;
-with Sem_Aux; use Sem_Aux;
-with Sem_Eval; use Sem_Eval;
-with Sem_Res; use Sem_Res;
-with Sem_Util; use Sem_Util;
-with Sem_Type; use Sem_Type;
-with Snames; use Snames;
-with Stand; use Stand;
-with Sinfo; use Sinfo;
-with Tbuild; use Tbuild;
-with Uintp; use Uintp;
+with Atree; use Atree;
+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 Namet; use Namet;
+with Nlists; use Nlists;
+with Nmake; use Nmake;
+with Opt; use Opt;
+with Sem; use Sem;
+with Sem_Aux; use Sem_Aux;
+with Sem_Eval; use Sem_Eval;
+with Sem_Res; use Sem_Res;
+with Sem_Util; use Sem_Util;
+with Sem_Type; use Sem_Type;
+with Snames; use Snames;
+with Stand; use Stand;
+with Sinfo; use Sinfo;
+with Sinfo.Nodes; use Sinfo.Nodes;
+with Sinfo.Utils; use Sinfo.Utils;
+with Table;
+with Tbuild; use Tbuild;
+with Uintp; use Uintp;
with Ada.Unchecked_Deallocation;
with GNAT.Heap_Sort_G;
+with GNAT.Sets;
package body Sem_Case is
@@ -84,13 +91,126 @@ package body Sem_Case is
--
-- Bounds_Type is the type whose range must be covered by the alternatives
--
- -- Subtyp is the subtype of the expression. If its bounds are non-static
+ -- Subtyp is the subtype of the expression. If its bounds are nonstatic
-- the alternatives must cover its base type.
function Choice_Image (Value : Uint; Ctype : Entity_Id) return Name_Id;
-- Given a Pos value of enumeration type Ctype, returns the name
-- ID of an appropriate string to be used in error message output.
+ function Has_Static_Discriminant_Constraint
+ (Subtyp : Entity_Id) return Boolean;
+ -- Returns True if the given subtype is subject to a discriminant
+ -- constraint and at least one of the constraint values is nonstatic.
+
+ package Composite_Case_Ops is
+
+ function Scalar_Part_Count (Subtyp : Entity_Id) return Nat;
+ -- Given the composite type Subtyp of a case selector, returns the
+ -- number of scalar parts in an object of this type. This is the
+ -- dimensionality of the associated Cartesian product space.
+
+ function Choice_Count (Alternatives : List_Id) return Nat;
+ -- The sum of the number of choices for each alternative in the given
+ -- list.
+
+ generic
+ Case_Statement : Node_Id;
+ package Choice_Analysis is
+
+ type Alternative_Id is
+ new Int range 1 .. List_Length (Alternatives (Case_Statement));
+ type Choice_Id is
+ new Int range 1 .. Choice_Count (Alternatives (Case_Statement));
+ type Part_Id is new Int range
+ 1 .. Scalar_Part_Count (Etype (Expression (Case_Statement)));
+
+ type Discrete_Range_Info is
+ record
+ Low, High : Uint;
+ end record;
+
+ type Composite_Range_Info is array (Part_Id) of Discrete_Range_Info;
+
+ type Choice_Range_Info (Is_Others : Boolean := False) is
+ record
+ case Is_Others is
+ when False =>
+ Ranges : Composite_Range_Info;
+ when True =>
+ null;
+ end case;
+ end record;
+
+ type Choices_Range_Info is array (Choice_Id) of Choice_Range_Info;
+
+ package Value_Sets is
+
+ type Value_Set is private;
+ -- A set of points in the Cartesian product space defined
+ -- by the composite type of the case selector.
+ -- Implemented as an access type.
+
+ type Set_Comparison is
+ (Disjoint, Equal, Contains, Contained_By, Overlaps);
+
+ function Compare (S1, S2 : Value_Set) return Set_Comparison;
+ -- If either argument (or both) is empty, result is Disjoint.
+ -- Otherwise, result is Equal if the two sets are equal.
+
+ Empty : constant Value_Set;
+
+ function Matching_Values
+ (Info : Composite_Range_Info) return Value_Set;
+ -- The Cartesian product of the given array of ranges
+ -- (excluding any values outside the Cartesian product of the
+ -- component ranges).
+
+ procedure Union (Target : in out Value_Set; Source : Value_Set);
+ -- Add elements of Source into Target
+
+ procedure Remove (Target : in out Value_Set; Source : Value_Set);
+ -- Remove elements of Source from Target
+
+ function Complement_Is_Empty (Set : Value_Set) return Boolean;
+ -- Return True iff the set is "maximal", in the sense that it
+ -- includes every value in the Cartesian product of the
+ -- component ranges.
+
+ procedure Free_Value_Sets;
+ -- Reclaim storage associated with implementation of this package.
+
+ private
+ type Value_Set is new Natural;
+ -- An index for a table that will be declared in the package body.
+
+ Empty : constant Value_Set := 0;
+
+ end Value_Sets;
+
+ type Single_Choice_Info (Is_Others : Boolean := False) is
+ record
+ Alternative : Alternative_Id;
+ case Is_Others is
+ when False =>
+ Matches : Value_Sets.Value_Set;
+ when True =>
+ null;
+ end case;
+ end record;
+
+ type Choices_Info is array (Choice_Id) of Single_Choice_Info;
+
+ function Analysis return Choices_Info;
+ -- Parse the case choices in order to determine the set of
+ -- matching values associated with each choice.
+
+ type Bound_Values is array (Positive range <>) of Node_Id;
+
+ end Choice_Analysis;
+
+ end Composite_Case_Ops;
+
procedure Expand_Others_Choice
(Case_Table : Choice_Table_Type;
Others_Choice : Node_Id;
@@ -141,9 +261,9 @@ package body Sem_Case is
-- is posted at location C. Caller sets Error_Msg_Sloc for xx.
procedure Explain_Non_Static_Bound;
- -- Called when we find a non-static bound, requiring the base type to
+ -- Called when we find a nonstatic bound, requiring the base type to
-- be covered. Provides where possible a helpful explanation of why the
- -- bounds are non-static, since this is not always obvious.
+ -- bounds are nonstatic, since this is not always obvious.
function Lt_Choice (C1, C2 : Natural) return Boolean;
-- Comparison routine for comparing Choice_Table entries. Use the lower
@@ -531,20 +651,23 @@ package body Sem_Case is
and then Compile_Time_Known_Value (C)
and then Expr_Value (C) = Lo
then
- Error_Msg_N ("duplication of choice value: &#!", C);
+ Error_Msg_N
+ ("duplication of choice value: &#!", Original_Node (C));
-- Not that special case, so just output the integer value
else
Error_Msg_Uint_1 := Lo;
- Error_Msg_N ("duplication of choice value: ^#!", C);
+ Error_Msg_N
+ ("duplication of choice value: ^#!", Original_Node (C));
end if;
-- Enumeration type
else
Error_Msg_Name_1 := Choice_Image (Lo, Bounds_Type);
- Error_Msg_N ("duplication of choice value: %#!", C);
+ Error_Msg_N
+ ("duplication of choice value: %#!", Original_Node (C));
end if;
-- More than one choice value, so print range of values
@@ -577,7 +700,9 @@ package body Sem_Case is
else
Error_Msg_Uint_1 := Lo;
Error_Msg_Uint_2 := Hi;
- Error_Msg_N ("duplication of choice values: ^ .. ^#!", C);
+ Error_Msg_N
+ ("duplication of choice values: ^ .. ^#!",
+ Original_Node (C));
end if;
-- Enumeration type
@@ -585,7 +710,8 @@ package body Sem_Case is
else
Error_Msg_Name_1 := Choice_Image (Lo, Bounds_Type);
Error_Msg_Name_2 := Choice_Image (Hi, Bounds_Type);
- Error_Msg_N ("duplication of choice values: % .. %#!", C);
+ Error_Msg_N
+ ("duplication of choice values: % .. %#!", Original_Node (C));
end if;
end if;
end Dup_Choice;
@@ -614,7 +740,7 @@ package body Sem_Case is
("bounds of & are not static, "
& "alternatives must cover base type!", Expr, Expr);
- -- If this is a case statement, the expression may be non-static
+ -- If this is a case statement, the expression may be nonstatic
-- or else the subtype may be at fault.
elsif Is_Entity_Name (Expr) then
@@ -677,8 +803,6 @@ package body Sem_Case is
--------------------
procedure Missing_Choice (Value1 : Uint; Value2 : Uint) is
- Msg_Sloc : constant Source_Ptr := Sloc (Case_Node);
-
begin
-- AI05-0188 : within an instance the non-others choices do not have
-- to belong to the actual subtype.
@@ -692,7 +816,7 @@ package body Sem_Case is
elsif Value1 > Value2 then
return;
- -- If predicate is already known to be violated, do no check for
+ -- If predicate is already known to be violated, do not check for
-- coverage error, to prevent cascaded messages.
elsif Predicate_Error then
@@ -704,10 +828,10 @@ package body Sem_Case is
if Value1 = Value2 then
if Is_Integer_Type (Bounds_Type) then
Error_Msg_Uint_1 := Value1;
- Error_Msg ("missing case value: ^!", Msg_Sloc);
+ Error_Msg_N ("missing case value: ^!", Case_Node);
else
Error_Msg_Name_1 := Choice_Image (Value1, Bounds_Type);
- Error_Msg ("missing case value: %!", Msg_Sloc);
+ Error_Msg_N ("missing case value: %!", Case_Node);
end if;
-- More than one choice value, so print range of values
@@ -716,11 +840,11 @@ package body Sem_Case is
if Is_Integer_Type (Bounds_Type) then
Error_Msg_Uint_1 := Value1;
Error_Msg_Uint_2 := Value2;
- Error_Msg ("missing case values: ^ .. ^!", Msg_Sloc);
+ Error_Msg_N ("missing case values: ^ .. ^!", Case_Node);
else
Error_Msg_Name_1 := Choice_Image (Value1, Bounds_Type);
Error_Msg_Name_2 := Choice_Image (Value2, Bounds_Type);
- Error_Msg ("missing case values: % .. %!", Msg_Sloc);
+ Error_Msg_N ("missing case values: % .. %!", Case_Node);
end if;
end if;
end Missing_Choice;
@@ -972,6 +1096,1264 @@ package body Sem_Case is
return Name_Find;
end Choice_Image;
+ package body Composite_Case_Ops is
+
+ function Static_Array_Length (Subtyp : Entity_Id) return Nat;
+ -- Given a one-dimensional constrained array subtype with
+ -- statically known bounds, return its length.
+
+ -------------------------
+ -- Static_Array_Length --
+ -------------------------
+
+ function Static_Array_Length (Subtyp : Entity_Id) return Nat is
+ pragma Assert (Is_Constrained (Subtyp));
+ pragma Assert (Number_Dimensions (Subtyp) = 1);
+ Index : constant Node_Id := First_Index (Subtyp);
+ pragma Assert (Is_OK_Static_Range (Index));
+ Lo : constant Uint := Expr_Value (Low_Bound (Index));
+ Hi : constant Uint := Expr_Value (High_Bound (Index));
+ Len : constant Uint := UI_Max (0, (Hi - Lo) + 1);
+ begin
+ return UI_To_Int (Len);
+ end Static_Array_Length;
+
+ -----------------------
+ -- Scalar_Part_Count --
+ -----------------------
+
+ function Scalar_Part_Count (Subtyp : Entity_Id) return Nat is
+ begin
+ if Is_Scalar_Type (Subtyp) then
+ return 1;
+ elsif Is_Array_Type (Subtyp) then
+ return Static_Array_Length (Subtyp)
+ * Scalar_Part_Count (Component_Type (Subtyp));
+ elsif Is_Record_Type (Subtyp) then
+ declare
+ Result : Nat := 0;
+ Comp : Entity_Id := First_Component_Or_Discriminant
+ (Base_Type (Subtyp));
+ begin
+ while Present (Comp) loop
+ Result := Result + Scalar_Part_Count (Etype (Comp));
+ Next_Component_Or_Discriminant (Comp);
+ end loop;
+ return Result;
+ end;
+ else
+ pragma Assert (False);
+ raise Program_Error;
+ end if;
+ end Scalar_Part_Count;
+
+ ------------------
+ -- Choice_Count --
+ ------------------
+
+ function Choice_Count (Alternatives : List_Id) return Nat is
+ Result : Nat := 0;
+ Alt : Node_Id := First (Alternatives);
+ begin
+ while Present (Alt) loop
+ Result := Result + List_Length (Discrete_Choices (Alt));
+ Next (Alt);
+ end loop;
+ return Result;
+ end Choice_Count;
+
+ package body Choice_Analysis is
+
+ function Component_Bounds_Info return Composite_Range_Info;
+ -- Returns the (statically known) bounds for each component.
+ -- The selector expression value (or any other value of the type
+ -- of the selector expression) can be thought of as a point in the
+ -- Cartesian product of these sets.
+
+ function Parse_Choice (Choice : Node_Id;
+ Alt : Node_Id) return Choice_Range_Info;
+ -- Extract Choice_Range_Info from a Choice node
+
+ ---------------------------
+ -- Component_Bounds_Info --
+ ---------------------------
+
+ function Component_Bounds_Info return Composite_Range_Info is
+ Result : Composite_Range_Info;
+ Next : Part_Id := 1;
+ Done : Boolean := False;
+
+ procedure Update_Result (Info : Discrete_Range_Info);
+ -- Initialize first remaining uninitialized element of Result.
+ -- Also set Next and Done.
+
+ -------------------
+ -- Update_Result --
+ -------------------
+
+ procedure Update_Result (Info : Discrete_Range_Info) is
+ begin
+ Result (Next) := Info;
+ if Next /= Part_Id'Last then
+ Next := Next + 1;
+ else
+ pragma Assert (not Done);
+ Done := True;
+ end if;
+ end Update_Result;
+
+ procedure Traverse_Discrete_Parts (Subtyp : Entity_Id);
+ -- Traverse the given subtype, looking for discrete parts.
+ -- For an array subtype of length N, the element subtype
+ -- is traversed N times. For a record subtype, traverse
+ -- each component's subtype (once). When a discrete part is
+ -- found, call Update_Result.
+
+ -----------------------------
+ -- Traverse_Discrete_Parts --
+ -----------------------------
+
+ procedure Traverse_Discrete_Parts (Subtyp : Entity_Id) is
+ begin
+ if Is_Discrete_Type (Subtyp) then
+ Update_Result
+ ((Low => Expr_Value (Type_Low_Bound (Subtyp)),
+ High => Expr_Value (Type_High_Bound (Subtyp))));
+ elsif Is_Array_Type (Subtyp) then
+ for I in 1 .. Static_Array_Length (Subtyp) loop
+ Traverse_Discrete_Parts (Component_Type (Subtyp));
+ end loop;
+ elsif Is_Record_Type (Subtyp) then
+ if Has_Static_Discriminant_Constraint (Subtyp) then
+
+ -- The component range for a constrained discriminant
+ -- is a single value.
+ declare
+ Dc_Elmt : Elmt_Id :=
+ First_Elmt (Discriminant_Constraint (Subtyp));
+ Dc_Value : Uint;
+ begin
+ while Present (Dc_Elmt) loop
+ Dc_Value := Expr_Value (Node (Dc_Elmt));
+ Update_Result ((Low => Dc_Value,
+ High => Dc_Value));
+
+ Next_Elmt (Dc_Elmt);
+ end loop;
+ end;
+
+ -- Generate ranges for nondiscriminant components.
+ declare
+ Comp : Entity_Id := First_Component
+ (Base_Type (Subtyp));
+ begin
+ while Present (Comp) loop
+ Traverse_Discrete_Parts (Etype (Comp));
+ Next_Component (Comp);
+ end loop;
+ end;
+ else
+ -- Generate ranges for all components
+ declare
+ Comp : Entity_Id :=
+ First_Component_Or_Discriminant
+ (Base_Type (Subtyp));
+ begin
+ while Present (Comp) loop
+ Traverse_Discrete_Parts (Etype (Comp));
+ Next_Component_Or_Discriminant (Comp);
+ end loop;
+ end;
+ end if;
+ else
+ Error_Msg_N
+ ("case selector type having a non-discrete non-record"
+ & " non-array subcomponent type not implemented",
+ Expression (Case_Statement));
+ end if;
+ end Traverse_Discrete_Parts;
+
+ begin
+ Traverse_Discrete_Parts (Etype (Expression (Case_Statement)));
+ pragma Assert (Done or else Serious_Errors_Detected > 0);
+ return Result;
+ end Component_Bounds_Info;
+
+ Component_Bounds : constant Composite_Range_Info
+ := Component_Bounds_Info;
+
+ package Case_Bindings is
+
+ procedure Note_Binding
+ (Comp_Assoc : Node_Id;
+ Choice : Node_Id;
+ Alt : Node_Id);
+ -- Note_Binding is called once for each component association
+ -- that defines a binding (using either "A => B is X" or
+ -- "A => <X>" syntax);
+
+ procedure Check_Bindings;
+ -- After all calls to Note_Binding, check that bindings are
+ -- ok (e.g., check consistency among different choices of
+ -- one alternative).
+
+ end Case_Bindings;
+
+ procedure Refresh_Binding_Info (Aggr : Node_Id);
+ -- The parser records binding-related info in the tree.
+ -- The choice nodes that we see here might not be (will never be?)
+ -- the original nodes that were produced by the parser. The info
+ -- recorded by the parser is missing in that case, so this
+ -- procedure recovers it.
+ --
+ -- There are bugs here. In some cases involving nested aggregates,
+ -- the path back to the parser-created nodes is lost. In particular,
+ -- we may fail to detect an illegal case like
+ -- when (F1 | F2 => (Aa => Natural, Bb => Natural is X)) =>
+ -- This should be rejected because it is binding X to both the
+ -- F1.Bb and to the F2.Bb subcomponents of the case selector.
+ -- It would be nice if the not-specific-to-pattern-matching
+ -- aggregate-processing code could remain unaware of the existence
+ -- of this binding-related info but perhaps that isn't possible.
+
+ --------------------------
+ -- Refresh_Binding_Info --
+ --------------------------
+
+ procedure Refresh_Binding_Info (Aggr : Node_Id) is
+ Orig_Aggr : constant Node_Id := Original_Node (Aggr);
+ Orig_Comp : Node_Id := First (Component_Associations (Orig_Aggr));
+ begin
+ if Aggr = Orig_Aggr then
+ return;
+ end if;
+
+ while Present (Orig_Comp) loop
+ if Nkind (Orig_Comp) = N_Component_Association
+ and then Binding_Chars (Orig_Comp) /= No_Name
+ then
+ if List_Length (Choices (Orig_Comp)) /= 1 then
+ -- Conceivably this could be checked during parsing,
+ -- but checking is easier here.
+
+ Error_Msg_N
+ ("binding shared by multiple components", Orig_Comp);
+ return;
+ end if;
+
+ declare
+ Orig_Name : constant Name_Id :=
+ Chars (First (Choices (Orig_Comp)));
+ Comp : Node_Id := First (Component_Associations (Aggr));
+ Matching_Comp : Node_Id := Empty;
+ begin
+ while Present (Comp) loop
+ if Chars (First (Choices (Comp))) = Orig_Name then
+ pragma Assert (not Present (Matching_Comp));
+ Matching_Comp := Comp;
+ end if;
+
+ Next (Comp);
+ end loop;
+
+ pragma Assert (Present (Matching_Comp));
+
+ Set_Binding_Chars
+ (Matching_Comp,
+ Binding_Chars (Orig_Comp));
+ end;
+ end if;
+
+ Next (Orig_Comp);
+ end loop;
+ end Refresh_Binding_Info;
+
+ ------------------
+ -- Parse_Choice --
+ ------------------
+
+ function Parse_Choice (Choice : Node_Id;
+ Alt : Node_Id) return Choice_Range_Info
+ is
+ Result : Choice_Range_Info (Is_Others => False);
+ Ranges : Composite_Range_Info renames Result.Ranges;
+ Next_Part : Part_Id'Base range 1 .. Part_Id'Last + 1 := 1;
+
+ procedure Traverse_Choice (Expr : Node_Id);
+ -- Traverse a legal choice expression, looking for
+ -- values/ranges of discrete parts. Call Update_Result
+ -- for each.
+
+ procedure Update_Result (Discrete_Range : Discrete_Range_Info);
+ -- Initialize first remaining uninitialized element of Ranges.
+ -- Also set Next_Part.
+
+ procedure Update_Result_For_Full_Coverage (Comp_Type : Entity_Id);
+ -- For each scalar part of the given component type, call
+ -- Update_Result with the full range for that scalar part.
+ -- This is used for both box components in aggregates and
+ -- for any inactive-variant components that do not appear in
+ -- a given aggregate.
+
+ -------------------
+ -- Update_Result --
+ -------------------
+
+ procedure Update_Result (Discrete_Range : Discrete_Range_Info) is
+ begin
+ Ranges (Next_Part) := Discrete_Range;
+ Next_Part := Next_Part + 1;
+ end Update_Result;
+
+ -------------------------------------
+ -- Update_Result_For_Full_Coverage --
+ -------------------------------------
+
+ procedure Update_Result_For_Full_Coverage (Comp_Type : Entity_Id)
+ is
+ begin
+ for Counter in 1 .. Scalar_Part_Count (Comp_Type) loop
+ Update_Result (Component_Bounds (Next_Part));
+ end loop;
+ end Update_Result_For_Full_Coverage;
+
+ ---------------------
+ -- Traverse_Choice --
+ ---------------------
+
+ procedure Traverse_Choice (Expr : Node_Id) is
+ begin
+ if Nkind (Expr) = N_Qualified_Expression then
+ Traverse_Choice (Expression (Expr));
+
+ elsif Nkind (Expr) = N_Type_Conversion
+ and then not Comes_From_Source (Expr)
+ then
+ if Expr /= Original_Node (Expr) then
+ Traverse_Choice (Original_Node (Expr));
+ else
+ Traverse_Choice (Expression (Expr));
+ end if;
+
+ elsif Nkind (Expr) = N_Aggregate then
+ if Is_Record_Type (Etype (Expr)) then
+ Refresh_Binding_Info (Aggr => Expr);
+
+ declare
+ Comp_Assoc : Node_Id :=
+ First (Component_Associations (Expr));
+ -- Aggregate has been normalized (components in
+ -- order, only one component per choice, etc.).
+
+ Comp_From_Type : Node_Id :=
+ First_Component_Or_Discriminant
+ (Base_Type (Etype (Expr)));
+
+ Saved_Next_Part : constant Part_Id := Next_Part;
+ begin
+ while Present (Comp_Assoc) loop
+ pragma Assert
+ (List_Length (Choices (Comp_Assoc)) = 1);
+
+ declare
+ Comp : constant Node_Id :=
+ Entity (First (Choices (Comp_Assoc)));
+ Comp_Seen : Boolean := False;
+ begin
+ loop
+ if Original_Record_Component (Comp) =
+ Original_Record_Component (Comp_From_Type)
+ then
+ Comp_Seen := True;
+ else
+ -- We have an aggregate of a type that
+ -- has a variant part (or has a
+ -- subcomponent type that has a variant
+ -- part) and we have to deal with a
+ -- component that is present in the type
+ -- but not in the aggregate (because the
+ -- component is in an inactive variant).
+ --
+ Update_Result_For_Full_Coverage
+ (Comp_Type => Etype (Comp_From_Type));
+ end if;
+
+ Comp_From_Type :=
+ Next_Component_Or_Discriminant
+ (Comp_From_Type);
+
+ exit when Comp_Seen;
+ end loop;
+ end;
+
+ if Box_Present (Comp_Assoc) then
+ -- Box matches all values
+ Update_Result_For_Full_Coverage
+ (Etype (First (Choices (Comp_Assoc))));
+ else
+ Traverse_Choice (Expression (Comp_Assoc));
+ end if;
+
+ if Binding_Chars (Comp_Assoc) /= No_Name
+ then
+ Case_Bindings.Note_Binding
+ (Comp_Assoc => Comp_Assoc,
+ Choice => Choice,
+ Alt => Alt);
+ end if;
+
+ Next (Comp_Assoc);
+ end loop;
+
+ while Present (Comp_From_Type) loop
+ -- Deal with any trailing inactive-variant
+ -- components.
+ --
+ -- See earlier commment about calling
+ -- Update_Result_For_Full_Coverage for such
+ -- components.
+
+ Update_Result_For_Full_Coverage
+ (Comp_Type => Etype (Comp_From_Type));
+
+ Comp_From_Type :=
+ Next_Component_Or_Discriminant (Comp_From_Type);
+ end loop;
+
+ pragma Assert
+ (Nat (Next_Part - Saved_Next_Part)
+ = Scalar_Part_Count (Etype (Expr)));
+ end;
+ elsif Is_Array_Type (Etype (Expr)) then
+ if Is_Non_Empty_List (Component_Associations (Expr)) then
+ Error_Msg_N
+ ("non-positional array aggregate as/within case "
+ & "choice not implemented", Expr);
+ end if;
+
+ declare
+ Subexpr : Node_Id := First (Expressions (Expr));
+ begin
+ while Present (Subexpr) loop
+ Traverse_Choice (Subexpr);
+ Next (Subexpr);
+ end loop;
+ end;
+ else
+ raise Program_Error;
+ end if;
+ elsif Is_Discrete_Type (Etype (Expr)) then
+ if Nkind (Expr) in N_Has_Entity and then
+ Is_Type (Entity (Expr))
+ then
+ declare
+ Low : constant Node_Id :=
+ Type_Low_Bound (Entity (Expr));
+ High : constant Node_Id :=
+ Type_High_Bound (Entity (Expr));
+ begin
+ Update_Result ((Low => Expr_Value (Low),
+ High => Expr_Value (High)));
+ end;
+ else
+ pragma Assert (Compile_Time_Known_Value (Expr));
+ Update_Result ((Low | High => Expr_Value (Expr)));
+ end if;
+ else
+ Error_Msg_N
+ ("non-aggregate case choice subexpression which is not"
+ & " of a discrete type not implemented", Expr);
+ end if;
+ end Traverse_Choice;
+
+ -- Start of processing for Parse_Choice
+
+ begin
+ if Nkind (Choice) = N_Others_Choice then
+ return (Is_Others => True);
+ end if;
+ Traverse_Choice (Choice);
+
+ -- Avoid returning uninitialized garbage in error case
+ if Next_Part /= Part_Id'Last + 1 then
+ pragma Assert (Serious_Errors_Detected > 0);
+ Result.Ranges := (others => (Low => Uint_1, High => Uint_0));
+ end if;
+
+ return Result;
+ end Parse_Choice;
+
+ package body Case_Bindings is
+
+ type Binding is record
+ Comp_Assoc : Node_Id;
+ Choice : Node_Id;
+ Alt : Node_Id;
+ end record;
+
+ type Binding_Index is new Natural;
+
+ package Case_Bindings_Table is new Table.Table
+ (Table_Component_Type => Binding,
+ Table_Index_Type => Binding_Index,
+ Table_Low_Bound => 1,
+ Table_Initial => 16,
+ Table_Increment => 64,
+ Table_Name => "Composite_Case_Ops.Case_Bindings");
+
+ ------------------
+ -- Note_Binding --
+ ------------------
+
+ procedure Note_Binding
+ (Comp_Assoc : Node_Id;
+ Choice : Node_Id;
+ Alt : Node_Id)
+ is
+ begin
+ Case_Bindings_Table.Append
+ ((Comp_Assoc => Comp_Assoc,
+ Choice => Choice,
+ Alt => Alt));
+ end Note_Binding;
+
+ --------------------
+ -- Check_Bindings --
+ --------------------
+
+ procedure Check_Bindings
+ is
+ use Case_Bindings_Table;
+ begin
+ if Last = 0 then
+ -- no bindings to check
+ return;
+ end if;
+
+ declare
+ Tab : Table_Type
+ renames Case_Bindings_Table.Table (1 .. Last);
+
+ function Same_Id (Idx1, Idx2 : Binding_Index)
+ return Boolean is (
+ Binding_Chars (Tab (Idx1).Comp_Assoc) =
+ Binding_Chars (Tab (Idx2).Comp_Assoc));
+
+ function Binding_Subtype (Idx : Binding_Index)
+ return Entity_Id is
+ (Etype (Nlists.First (Choices (Tab (Idx).Comp_Assoc))));
+ begin
+ -- Verify that elements with given choice or alt value
+ -- are contiguous, and that elements with equal
+ -- choice values have same alt value.
+
+ for Idx1 in 2 .. Tab'Last loop
+ if Tab (Idx1 - 1).Choice /= Tab (Idx1).Choice then
+ pragma Assert
+ (for all Idx2 in Idx1 + 1 .. Tab'Last =>
+ Tab (Idx2).Choice /= Tab (Idx1 - 1).Choice);
+ else
+ pragma Assert (Tab (Idx1 - 1).Alt = Tab (Idx1).Alt);
+ end if;
+ if Tab (Idx1 - 1).Alt /= Tab (Idx1).Alt then
+ pragma Assert
+ (for all Idx2 in Idx1 + 1 .. Tab'Last =>
+ Tab (Idx2).Alt /= Tab (Idx1 - 1).Alt);
+ end if;
+ end loop;
+
+ -- Check for user errors:
+ -- 1) Two choices for a given alternative shall define the
+ -- same set of names. Can't have
+ -- when (<X>, 0) | (0, <Y>) =>
+ -- 2) A choice shall not define a name twice. Can't have
+ -- when (A => <X>, B => <X>, C => 0) =>
+ -- 3) Two definitions of a name within one alternative
+ -- shall have statically matching component subtypes.
+ -- Can't have
+ -- type R is record Int : Integer;
+ -- Nat : Natural; end record;
+ -- case R'(...) is
+ -- when (<X>, 1) | (1, <X>) =>
+ -- 4) A given binding shall match only one value.
+ -- Can't have
+ -- (Fld1 | Fld2 => (Fld => <X>))
+ -- For now, this is enforced *very* conservatively
+ -- with respect to arrays - a binding cannot match
+ -- any part of an array. This is temporary.
+
+ for Idx1 in Tab'Range loop
+ if Idx1 = 1
+ or else Tab (Idx1 - 1).Alt /= Tab (Idx1).Alt
+ then
+ -- Process one alternative
+ declare
+ Alt_Start : constant Binding_Index := Idx1;
+ Alt : constant Node_Id := Tab (Alt_Start).Alt;
+
+ First_Choice : constant Node_Id :=
+ Nlists.First (Discrete_Choices (Alt));
+ First_Choice_Bindings : Natural := 0;
+ begin
+ -- Check for duplicates within one choice,
+ -- and for choices with no bindings.
+
+ if First_Choice /= Tab (Alt_Start).Choice then
+ Error_Msg_N ("binding(s) missing for choice",
+ First_Choice);
+ return;
+ end if;
+
+ declare
+ Current_Choice : Node_Id := First_Choice;
+ Choice_Start : Binding_Index := Alt_Start;
+ begin
+ for Idx2 in Alt_Start .. Tab'Last loop
+ exit when Tab (Idx2).Alt /= Alt;
+ if Tab (Idx2).Choice = Current_Choice then
+ for Idx3 in Choice_Start .. Idx2 - 1 loop
+ if Same_Id (Idx2, Idx3)
+ then
+ Error_Msg_N
+ ("duplicate binding in choice",
+ Current_Choice);
+ return;
+ end if;
+ end loop;
+ else
+ Next (Current_Choice);
+ pragma Assert (Present (Current_Choice));
+ Choice_Start := Idx2;
+
+ if Tab (Idx2).Choice /= Current_Choice
+ then
+ Error_Msg_N
+ ("binding(s) missing for choice",
+ Current_Choice);
+ return;
+ end if;
+ end if;
+ end loop;
+
+ -- If we made it through all the bindings
+ -- for this alternative but didn't make it
+ -- to the last choice, then bindings are
+ -- missing for all remaining choices.
+ -- We only complain about the first one.
+
+ if Present (Next (Current_Choice)) then
+ Error_Msg_N
+ ("binding(s) missing for choice",
+ Next (Current_Choice));
+ return;
+ end if;
+ end;
+
+ -- Count bindings for first choice of alternative
+
+ for FC_Idx in Alt_Start .. Tab'Last loop
+ exit when Tab (FC_Idx).Choice /= First_Choice;
+ First_Choice_Bindings :=
+ First_Choice_Bindings + 1;
+ end loop;
+
+ declare
+ Current_Choice : Node_Id := First_Choice;
+ Current_Choice_Bindings : Natural := 0;
+ begin
+ for Idx2 in Alt_Start .. Tab'Last loop
+ exit when Tab (Idx2).Alt /= Alt;
+
+ -- If starting a new choice
+
+ if Tab (Idx2).Choice /= Current_Choice then
+
+ -- Check count for choice just finished
+
+ if Current_Choice_Bindings
+ /= First_Choice_Bindings
+ then
+ Error_Msg_N
+ ("subsequent choice has different"
+ & " number of bindings than first"
+ & " choice", Current_Choice);
+ end if;
+
+ Current_Choice := Tab (Idx2).Choice;
+ Current_Choice_Bindings := 1;
+
+ -- Remember that Alt has both one or more
+ -- bindings and two or more choices; we'll
+ -- need to know this during expansion.
+
+ Set_Multidefined_Bindings (Alt, True);
+ else
+ Current_Choice_Bindings :=
+ Current_Choice_Bindings + 1;
+ end if;
+
+ -- Check that first choice has binding with
+ -- matching name; check subtype consistency.
+
+ declare
+ Found : Boolean := False;
+ begin
+ for FC_Idx in
+ Alt_Start ..
+ Alt_Start + Binding_Index
+ (First_Choice_Bindings - 1)
+ loop
+ if Same_Id (Idx2, FC_Idx) then
+ if not Subtypes_Statically_Match
+ (Binding_Subtype (Idx2),
+ Binding_Subtype (FC_Idx))
+ then
+ Error_Msg_N
+ ("subtype of binding in "
+ & "subsequent choice does not "
+ & "match that in first choice",
+ Tab (Idx2).Comp_Assoc);
+ end if;
+ Found := True;
+ exit;
+ end if;
+ end loop;
+
+ if not Found then
+ Error_Msg_N
+ ("binding defined in subsequent "
+ & "choice not defined in first "
+ & "choice", Current_Choice);
+ end if;
+ end;
+
+ -- Check for illegal repeated binding
+ -- via an enclosing aggregate, as in
+ -- (F1 | F2 => (F3 => Natural is X,
+ -- F4 => Natural))
+ -- where the inner aggregate would be ok.
+
+ declare
+ Rover : Node_Id := Tab (Idx2).Comp_Assoc;
+ begin
+ while Rover /= Tab (Idx2).Choice loop
+ Rover :=
+ (if Is_List_Member (Rover) then
+ Parent (List_Containing (Rover))
+ else Parent (Rover));
+ pragma Assert (Present (Rover));
+ if Nkind (Rover)
+ = N_Component_Association
+ and then List_Length (Choices (Rover))
+ > 1
+ then
+ Error_Msg_N
+ ("binding shared by multiple "
+ & "enclosing components",
+ Tab (Idx2).Comp_Assoc);
+ end if;
+ end loop;
+ end;
+ end loop;
+ end;
+
+ -- Construct the (unanalyzed) declarations for
+ -- the current alternative. Then analyze them.
+
+ if First_Choice_Bindings > 0 then
+ declare
+ Loc : constant Source_Ptr := Sloc (Alt);
+ Declarations : constant List_Id := New_List;
+ Decl : Node_Id;
+ begin
+ for FC_Idx in
+ Alt_Start ..
+ Alt_Start +
+ Binding_Index (First_Choice_Bindings - 1)
+ loop
+ Decl := Make_Object_Declaration
+ (Sloc => Loc,
+ Defining_Identifier =>
+ Make_Defining_Identifier
+ (Loc,
+ Binding_Chars
+ (Tab (FC_Idx).Comp_Assoc)),
+ Object_Definition =>
+ New_Occurrence_Of
+ (Binding_Subtype (FC_Idx), Loc));
+
+ Append_To (Declarations, Decl);
+ end loop;
+
+ declare
+ Old_Statements : constant List_Id :=
+ Statements (Alt);
+ New_Statements : constant List_Id :=
+ New_List;
+
+ Block_Statement : constant Node_Id :=
+ Make_Block_Statement (Sloc => Loc,
+ Declarations => Declarations,
+ Handled_Statement_Sequence =>
+ Make_Handled_Sequence_Of_Statements
+ (Loc, Old_Statements),
+ Has_Created_Identifier => True);
+ begin
+ Append_To
+ (New_Statements, Block_Statement);
+
+ Set_Statements (Alt, New_Statements);
+ end;
+ end;
+ end if;
+ end;
+ end if;
+ end loop;
+ end;
+ end Check_Bindings;
+ end Case_Bindings;
+
+ function Choice_Bounds_Info return Choices_Range_Info;
+ -- Returns mapping from any given Choice_Id value to that choice's
+ -- component-to-range map.
+
+ ------------------------
+ -- Choice_Bounds_Info --
+ ------------------------
+
+ function Choice_Bounds_Info return Choices_Range_Info is
+ Result : Choices_Range_Info;
+ Alt : Node_Id := First (Alternatives (Case_Statement));
+ C_Id : Choice_Id := 1;
+ begin
+ while Present (Alt) loop
+ declare
+ Choice : Node_Id := First (Discrete_Choices (Alt));
+ begin
+ while Present (Choice) loop
+ Result (C_Id) := Parse_Choice (Choice, Alt => Alt);
+
+ Next (Choice);
+ if C_Id /= Choice_Id'Last then
+ C_Id := C_Id + 1;
+ end if;
+ end loop;
+ end;
+ Next (Alt);
+ end loop;
+
+ pragma Assert (C_Id = Choice_Id'Last);
+
+ -- No more calls to Note_Binding, so time for checks.
+ Case_Bindings.Check_Bindings;
+
+ return Result;
+ end Choice_Bounds_Info;
+
+ Choices_Bounds : constant Choices_Range_Info := Choice_Bounds_Info;
+
+ package body Value_Sets is
+ use GNAT;
+
+ function Hash (Key : Uint) return Bucket_Range_Type is
+ (Bucket_Range_Type
+ (UI_To_Int (Key mod (Uint_2 ** Uint_31))));
+
+ package Uint_Sets is new GNAT.Sets.Membership_Sets
+ (Uint, "=", Hash);
+
+ type Representative_Values_Array is
+ array (Part_Id) of Uint_Sets.Membership_Set;
+
+ function Representative_Values_Init
+ return Representative_Values_Array;
+ -- Select the representative values for each Part_Id value.
+ -- This function is called exactly once, immediately after it
+ -- is declared.
+
+ --------------------------------
+ -- Representative_Values_Init --
+ --------------------------------
+
+ function Representative_Values_Init
+ return Representative_Values_Array
+ is
+ -- For each range of each choice (as well as the range for the
+ -- component subtype, which is handled in the first loop),
+ -- insert the low bound of the range and the successor of
+ -- the high bound into the corresponding R_V element.
+ --
+ -- The idea we are trying to capture here is somewhat tricky.
+ -- Given an arbitrary point P1 in the Cartesian product
+ -- of the Component_Bounds sets, we want to be able
+ -- to map that to a point P2 in the (smaller) Cartesian product
+ -- of the Representative_Values sets that has the property
+ -- that for every choice of the case statement, P1 matches
+ -- the choice if and only if P2 also matches. Given that,
+ -- we can implement the overlapping/containment/etc. rules
+ -- safely by just looking at (using brute force enumeration)
+ -- the (smaller) Cartesian product of the R_V sets.
+ -- We are never going to actually perform this point-to-point
+ -- mapping - just the fact that it exists is enough to ensure
+ -- we can safely look at just the R_V sets.
+ --
+ -- The desired mapping can be implemented by mapping a point
+ -- P1 to a point P2 by reducing each of P1's coordinates down
+ -- to the largest element of the corresponding R_V set that is
+ -- less than or equal to the original coordinate value (such
+ -- an element Y will always exist because the R_V set for a
+ -- given component always includes the low bound of the
+ -- component subtype). It then suffices to show that every
+ -- choice in the case statement yields the same Boolean result
+ -- for P1 as for P2.
+ --
+ -- Suppose the contrary. Then there is some particular
+ -- coordinate position X (i.e., a Part_Id value) and some
+ -- choice C where exactly one of P1(X) and P2(X) belongs to
+ -- the (contiguous) range associated with C(X); call that
+ -- range L .. H. We know that P2(X) <= P1(X) because the
+ -- mapping never increases coordinate values. Consider three
+ -- cases: P1(X) lies within the L .. H range, or it is greater
+ -- than H, or it is lower than L.
+ -- The third case is impossible because reducing a value that
+ -- is less than L can only produce another such value,
+ -- violating the "exactly one" assumption. The second
+ -- case is impossible because L belongs to the corresponding
+ -- R_V set, so P2(X) >= L and both values belong to the
+ -- range, again violating the "exactly one" assumption.
+ -- Finally, the third case is impossible because H+1 belongs
+ -- to the corresponding R_V set, so P2(X) > H, so neither
+ -- value belongs to the range, again violating the "exactly
+ -- one" assumption. So our initial supposition was wrong. QED.
+
+ use Uint_Sets;
+
+ Result : constant Representative_Values_Array
+ := (others => Uint_Sets.Create (Initial_Size => 32));
+
+ procedure Insert_Representative (Value : Uint; P : Part_Id);
+ -- Insert the given Value into the representative values set
+ -- for the given component if it belongs to the component's
+ -- subtype. Otherwise, do nothing.
+
+ ---------------------------
+ -- Insert_Representative --
+ ---------------------------
+
+ procedure Insert_Representative (Value : Uint; P : Part_Id) is
+ begin
+ if Value >= Component_Bounds (P).Low and
+ Value <= Component_Bounds (P).High
+ then
+ Insert (Result (P), Value);
+ end if;
+ end Insert_Representative;
+
+ begin
+ for P in Part_Id loop
+ Insert_Representative (Component_Bounds (P).Low, P);
+ end loop;
+ for C of Choices_Bounds loop
+ if not C.Is_Others then
+ for P in Part_Id loop
+ if C.Ranges (P).Low <= C.Ranges (P).High then
+ Insert_Representative (C.Ranges (P).Low, P);
+ Insert_Representative (C.Ranges (P).High + 1, P);
+ end if;
+ end loop;
+ end if;
+ end loop;
+ return Result;
+ end Representative_Values_Init;
+
+ Representative_Values : constant Representative_Values_Array
+ := Representative_Values_Init;
+ -- We want to avoid looking at every point in the Cartesian
+ -- product of all component values. Instead we select, for each
+ -- component, a set of representative values and then look only
+ -- at the Cartesian product of those sets. A single value can
+ -- safely represent a larger enclosing interval if every choice
+ -- for that component either completely includes or completely
+ -- excludes the interval. The elements of this array will be
+ -- populated by a call to Initialize_Representative_Values and
+ -- will remain constant after that.
+
+ type Value_Index_Base is new Natural;
+
+ function Value_Index_Count return Value_Index_Base;
+ -- Returns the product of the sizes of the Representative_Values
+ -- sets (i.e., the size of the Cartesian product of the sets).
+ -- May return zero if one of the sets is empty.
+ -- This function is called exactly once, immediately after it
+ -- is declared.
+
+ -----------------------
+ -- Value_Index_Count --
+ -----------------------
+
+ function Value_Index_Count return Value_Index_Base is
+ Result : Value_Index_Base := 1;
+ begin
+ for Set of Representative_Values loop
+ Result := Result * Value_Index_Base (Uint_Sets.Size (Set));
+ end loop;
+ return Result;
+ end Value_Index_Count;
+
+ Max_Value_Index : constant Value_Index_Base := Value_Index_Count;
+
+ subtype Value_Index is Value_Index_Base range 1 .. Max_Value_Index;
+ type Value_Index_Set is array (Value_Index) of Boolean;
+
+ package Value_Index_Set_Table is new Table.Table
+ (Table_Component_Type => Value_Index_Set,
+ Table_Index_Type => Value_Set,
+ Table_Low_Bound => 1,
+ Table_Initial => 16,
+ Table_Increment => 100,
+ Table_Name => "Composite_Case_Ops.Value_Sets");
+ -- A nonzero Value_Set value is an index into this table.
+
+ function Indexed (Index : Value_Set) return Value_Index_Set
+ is (Value_Index_Set_Table.Table.all (Index));
+
+ function Allocate_Table_Element (Initial_Value : Value_Index_Set)
+ return Value_Set;
+ -- Allocate and initialize a new table element; return its index.
+
+ ----------------------------
+ -- Allocate_Table_Element --
+ ----------------------------
+
+ function Allocate_Table_Element (Initial_Value : Value_Index_Set)
+ return Value_Set
+ is
+ use Value_Index_Set_Table;
+ begin
+ Append (Initial_Value);
+ return Last;
+ end Allocate_Table_Element;
+
+ procedure Assign_Table_Element (Index : Value_Set;
+ Value : Value_Index_Set);
+ -- Assign specified value to specified table element.
+
+ --------------------------
+ -- Assign_Table_Element --
+ --------------------------
+
+ procedure Assign_Table_Element (Index : Value_Set;
+ Value : Value_Index_Set)
+ is
+ begin
+ Value_Index_Set_Table.Table.all (Index) := Value;
+ end Assign_Table_Element;
+
+ -------------
+ -- Compare --
+ -------------
+
+ function Compare (S1, S2 : Value_Set) return Set_Comparison is
+ begin
+ if S1 = Empty or S2 = Empty then
+ return Disjoint;
+ elsif Indexed (S1) = Indexed (S2) then
+ return Equal;
+ else
+ declare
+ Intersection : constant Value_Index_Set
+ := Indexed (S1) and Indexed (S2);
+ begin
+ if (for all Flag of Intersection => not Flag) then
+ return Disjoint;
+ elsif Intersection = Indexed (S1) then
+ return Contained_By;
+ elsif Intersection = Indexed (S2) then
+ return Contains;
+ else
+ return Overlaps;
+ end if;
+ end;
+ end if;
+ end Compare;
+
+ -------------------------
+ -- Complement_Is_Empty --
+ -------------------------
+
+ function Complement_Is_Empty (Set : Value_Set) return Boolean
+ is (Set /= Empty
+ and then (for all Flag of Indexed (Set) => Flag));
+
+ ---------------------
+ -- Free_Value_Sets --
+ ---------------------
+ procedure Free_Value_Sets is
+ begin
+ Value_Index_Set_Table.Free;
+ end Free_Value_Sets;
+
+ -----------
+ -- Union --
+ -----------
+
+ procedure Union (Target : in out Value_Set; Source : Value_Set) is
+ begin
+ if Source /= Empty then
+ if Target = Empty then
+ Target := Allocate_Table_Element (Indexed (Source));
+ else
+ Assign_Table_Element
+ (Target, Indexed (Target) or Indexed (Source));
+ end if;
+ end if;
+ end Union;
+
+ ------------
+ -- Remove --
+ ------------
+
+ procedure Remove (Target : in out Value_Set; Source : Value_Set) is
+ begin
+ if Source /= Empty and Target /= Empty then
+ Assign_Table_Element
+ (Target, Indexed (Target) and not Indexed (Source));
+ if (for all V of Indexed (Target) => not V) then
+ Target := Empty;
+ end if;
+ end if;
+ end Remove;
+
+ ---------------------
+ -- Matching_Values --
+ ---------------------
+
+ function Matching_Values
+ (Info : Composite_Range_Info) return Value_Set
+ is
+ Matches : Value_Index_Set;
+ Next_Index : Value_Index := 1;
+ Done : Boolean := False;
+ Point : array (Part_Id) of Uint;
+
+ procedure Test_Point_For_Match;
+ -- Point identifies a point in the Cartesian product of the
+ -- representative value sets. Record whether that Point
+ -- belongs to the product-of-ranges specified by Info.
+
+ --------------------------
+ -- Test_Point_For_Match --
+ --------------------------
+
+ procedure Test_Point_For_Match is
+ function In_Range (Val : Uint; Rang : Discrete_Range_Info)
+ return Boolean is
+ ((Rang.Low <= Val) and then (Val <= Rang.High));
+ begin
+ pragma Assert (not Done);
+ Matches (Next_Index) :=
+ (for all P in Part_Id => In_Range (Point (P), Info (P)));
+ if Next_Index = Matches'Last then
+ Done := True;
+ else
+ Next_Index := Next_Index + 1;
+ end if;
+ end Test_Point_For_Match;
+
+ procedure Test_Points (P : Part_Id);
+ -- Iterate over the Cartesian product of the representative
+ -- value sets, calling Test_Point_For_Match for each point.
+
+ -----------------
+ -- Test_Points --
+ -----------------
+
+ procedure Test_Points (P : Part_Id) is
+ use Uint_Sets;
+ Iter : Iterator := Iterate (Representative_Values (P));
+ begin
+ -- We could traverse here in sorted order, as opposed to
+ -- whatever order the set iterator gives us.
+ -- No need for that as long as every iteration over
+ -- a given representative values set yields the same order.
+ -- Not sorting is more efficient, but it makes it harder to
+ -- interpret a Value_Index_Set bit vector when debugging.
+
+ while Has_Next (Iter) loop
+ Next (Iter, Point (P));
+
+ -- If we have finished building up a Point value, then
+ -- test it for matching. Otherwise, recurse to continue
+ -- building up a point value.
+
+ if P = Part_Id'Last then
+ Test_Point_For_Match;
+ else
+ Test_Points (P + 1);
+ end if;
+ end loop;
+ end Test_Points;
+
+ begin
+ Test_Points (1);
+ if (for all Flag of Matches => not Flag) then
+ return Empty;
+ end if;
+ return Allocate_Table_Element (Matches);
+ end Matching_Values;
+
+ end Value_Sets;
+
+ --------------
+ -- Analysis --
+ --------------
+
+ function Analysis return Choices_Info is
+ Result : Choices_Info;
+ Alt : Node_Id := First (Alternatives (Case_Statement));
+ A_Id : Alternative_Id := 1;
+ C_Id : Choice_Id := 1;
+ begin
+ while Present (Alt) loop
+ declare
+ Choice : Node_Id := First (Discrete_Choices (Alt));
+ begin
+ while Present (Choice) loop
+ if Nkind (Choice) = N_Others_Choice then
+ pragma Assert (Choices_Bounds (C_Id).Is_Others);
+ Result (C_Id) :=
+ (Alternative => A_Id,
+ Is_Others => True);
+ else
+ Result (C_Id) :=
+ (Alternative => A_Id,
+ Is_Others => False,
+ Matches => Value_Sets.Matching_Values
+ (Choices_Bounds (C_Id).Ranges));
+ end if;
+ Next (Choice);
+ if C_Id /= Choice_Id'Last then
+ C_Id := C_Id + 1;
+ end if;
+ end loop;
+ end;
+
+ Next (Alt);
+ if A_Id /= Alternative_Id'Last then
+ A_Id := A_Id + 1;
+ end if;
+ end loop;
+
+ pragma Assert (A_Id = Alternative_Id'Last);
+ pragma Assert (C_Id = Choice_Id'Last);
+
+ return Result;
+ end Analysis;
+
+ end Choice_Analysis;
+
+ end Composite_Case_Ops;
+
--------------------------
-- Expand_Others_Choice --
--------------------------
@@ -1065,9 +2447,10 @@ package body Sem_Case is
if Is_Standard_Character_Type (Choice_Type) then
Set_Character_Literal_Name (Char_Code (UI_To_Int (Value)));
- Lit := New_Node (N_Character_Literal, Loc);
- Set_Chars (Lit, Name_Find);
- Set_Char_Literal_Value (Lit, Value);
+ Lit :=
+ Make_Character_Literal (Loc,
+ Chars => Name_Find,
+ Char_Literal_Value => Value);
Set_Etype (Lit, Choice_Type);
Set_Is_Static_Expression (Lit, True);
return Lit;
@@ -1315,10 +2698,10 @@ package body Sem_Case is
-------------------
procedure Check_Choices
- (N : Node_Id;
- Alternatives : List_Id;
- Subtyp : Entity_Id;
- Others_Present : out Boolean)
+ (N : Node_Id;
+ Alternatives : List_Id;
+ Subtyp : Entity_Id;
+ Others_Present : out Boolean)
is
E : Entity_Id;
@@ -1370,6 +2753,15 @@ package body Sem_Case is
-- later entry into the choices table so that they can be sorted
-- later on.
+ procedure Check_Case_Pattern_Choices;
+ -- Check choices validity for the Ada extension case where the
+ -- selecting expression is not of a discrete type and so the
+ -- choices are patterns.
+
+ procedure Check_Composite_Case_Selector;
+ -- Check that the (non-discrete) type of the expression being
+ -- cased on is suitable.
+
procedure Handle_Static_Predicate
(Typ : Entity_Id;
Lo : Node_Id;
@@ -1491,6 +2883,209 @@ package body Sem_Case is
Num_Choices := Num_Choices + 1;
end Check;
+ --------------------------------
+ -- Check_Case_Pattern_Choices --
+ --------------------------------
+
+ procedure Check_Case_Pattern_Choices is
+ -- ??? Need to Free/Finalize value sets allocated here.
+
+ package Ops is new Composite_Case_Ops.Choice_Analysis
+ (Case_Statement => N);
+ use Ops;
+ use Ops.Value_Sets;
+
+ Empty : Value_Set renames Value_Sets.Empty;
+ -- Cope with hiding due to multiple use clauses
+
+ Info : constant Choices_Info := Analysis;
+ Others_Seen : Boolean := False;
+
+ begin
+ declare
+ Matches : array (Alternative_Id) of Value_Sets.Value_Set :=
+ (others => Empty);
+
+ Flag_Overlapping_Within_One_Alternative : constant Boolean :=
+ False;
+ -- We may want to flag overlapping (perhaps with only a
+ -- warning) if the pattern binds an identifier, as in
+ -- when (Positive, <X>) | (Integer, <X>) =>
+
+ Covered : Value_Set := Empty;
+ -- The union of all alternatives seen so far
+
+ begin
+ for Choice of Info loop
+ if Choice.Is_Others then
+ Others_Seen := True;
+ else
+ if Flag_Overlapping_Within_One_Alternative
+ and then (Compare (Matches (Choice.Alternative),
+ Choice.Matches) /= Disjoint)
+ then
+ Error_Msg_N
+ ("bad overlapping within one alternative", N);
+ end if;
+
+ Union (Target => Matches (Choice.Alternative),
+ Source => Choice.Matches);
+ end if;
+ end loop;
+
+ for A1 in Alternative_Id loop
+ for A2 in Alternative_Id
+ range A1 + 1 .. Alternative_Id'Last
+ loop
+ case Compare (Matches (A1), Matches (A2)) is
+ when Disjoint | Contained_By =>
+ null; -- OK
+ when Overlaps =>
+ declare
+ Uncovered_1, Uncovered_2 : Value_Set := Empty;
+ begin
+ Union (Uncovered_1, Matches (A1));
+ Remove (Uncovered_1, Covered);
+ Union (Uncovered_2, Matches (A2));
+ Remove (Uncovered_2, Covered);
+
+ -- Recheck for overlap after removing choices
+ -- covered by earlier alternatives.
+
+ case Compare (Uncovered_1, Uncovered_2) is
+ when Disjoint | Contained_By =>
+ null;
+ when Contains | Overlaps | Equal =>
+ Error_Msg_N
+ ("bad alternative overlapping", N);
+ end case;
+ end;
+
+ when Equal =>
+ Error_Msg_N ("alternatives match same values", N);
+ when Contains =>
+ Error_Msg_N ("alternatives in wrong order", N);
+ end case;
+ end loop;
+
+ Union (Target => Covered, Source => Matches (A1));
+ end loop;
+
+ if (not Others_Seen) and then not Complement_Is_Empty (Covered)
+ then
+ Error_Msg_N ("not all values are covered", N);
+ end if;
+ end;
+
+ Ops.Value_Sets.Free_Value_Sets;
+ end Check_Case_Pattern_Choices;
+
+ -----------------------------------
+ -- Check_Composite_Case_Selector --
+ -----------------------------------
+
+ procedure Check_Composite_Case_Selector is
+ -- Some of these restrictions will be relaxed eventually, but best
+ -- to initially err in the direction of being too restrictive.
+
+ procedure Check_Component_Subtype (Subtyp : Entity_Id);
+ -- Recursively traverse subcomponent types to perform checks.
+
+ -----------------------------
+ -- Check_Component_Subtype --
+ -----------------------------
+
+ procedure Check_Component_Subtype (Subtyp : Entity_Id) is
+ begin
+ if Has_Predicates (Subtyp) then
+ Error_Msg_N
+ ("subtype of case selector (or subcomponent thereof) " &
+ "has predicate", N);
+ elsif Is_Discrete_Type (Subtyp) then
+ if not Is_Static_Subtype (Subtyp) then
+ Error_Msg_N
+ ("discrete subtype of selector subcomponent is not " &
+ "a static subtype", N);
+ elsif Is_Enumeration_Type (Subtyp)
+ and then Has_Enumeration_Rep_Clause (Subtyp)
+ then
+ Error_Msg_N
+ ("enumeration type of selector subcomponent has " &
+ "an enumeration representation clause", N);
+ end if;
+ elsif Is_Array_Type (Subtyp) then
+ pragma Assert (Is_Constrained (Subtyp));
+
+ if Number_Dimensions (Subtyp) /= 1 then
+ Error_Msg_N
+ ("dimensionality of array type of case selector (or " &
+ "subcomponent thereof) is greater than 1", N);
+ elsif not Is_OK_Static_Range (First_Index (Subtyp)) then
+ Error_Msg_N
+ ("array subtype of case selector (or " &
+ "subcomponent thereof) has nonstatic constraint", N);
+ end if;
+ Check_Component_Subtype (Component_Type (Subtyp));
+ elsif Is_Record_Type (Subtyp) then
+
+ if Has_Discriminants (Subtyp)
+ and then Is_Constrained (Subtyp)
+ and then not Has_Static_Discriminant_Constraint (Subtyp)
+ then
+ -- We are only disallowing nonstatic constraints for
+ -- subcomponent subtypes, not for the subtype of the
+ -- expression we are casing on. This test could be
+ -- implemented via an Is_Recursive_Call parameter if
+ -- that seems preferable.
+
+ if Subtyp /= Check_Choices.Subtyp then
+ Error_Msg_N
+ ("constrained discriminated subtype of case " &
+ "selector subcomponent has nonstatic " &
+ "constraint", N);
+ end if;
+ end if;
+
+ declare
+ Comp : Entity_Id :=
+ First_Component_Or_Discriminant (Base_Type (Subtyp));
+ begin
+ while Present (Comp) loop
+ Check_Component_Subtype (Etype (Comp));
+ Next_Component_Or_Discriminant (Comp);
+ end loop;
+ end;
+ else
+ Error_Msg_N
+ ("type of case selector (or subcomponent thereof) is " &
+ "not a discrete type, a record type, or an array type",
+ N);
+ end if;
+ end Check_Component_Subtype;
+
+ begin
+ if not Is_Composite_Type (Subtyp) then
+ Error_Msg_N
+ ("case selector type neither discrete nor composite", N);
+
+ elsif Is_Limited_Type (Subtyp) then
+ Error_Msg_N ("case selector type is limited", N);
+
+ elsif Is_Class_Wide_Type (Subtyp) then
+ Error_Msg_N ("case selector type is class-wide", N);
+
+ elsif Needs_Finalization (Subtyp) then
+ Error_Msg_N ("case selector type requires finalization", N);
+
+ elsif Is_Array_Type (Subtyp) and not Is_Constrained (Subtyp) then
+ Error_Msg_N
+ ("case selector subtype is unconstrained array subtype", N);
+
+ else
+ Check_Component_Subtype (Subtyp);
+ end if;
+ end Check_Composite_Case_Selector;
+
-----------------------------
-- Handle_Static_Predicate --
-----------------------------
@@ -1523,6 +3118,7 @@ package body Sem_Case is
then
C := New_Copy (P);
Set_Sloc (C, Sloc (Choice));
+ Set_Original_Node (C, Choice);
if Expr_Value (Low_Bound (C)) < Expr_Value (Lo) then
Set_Low_Bound (C, Lo);
@@ -1552,6 +3148,14 @@ package body Sem_Case is
-- a complete mess.
if not Is_Discrete_Type (Subtyp) or else Subtyp = Any_Type then
+
+ -- Hold on, maybe it isn't a complete mess after all.
+
+ if Extensions_Allowed and then Subtyp /= Any_Type then
+ Check_Composite_Case_Selector;
+ Check_Case_Pattern_Choices;
+ end if;
+
return;
end if;
@@ -1559,7 +3163,7 @@ package body Sem_Case is
-- bounds of its base type to determine the values covered by the
-- discrete choices.
- -- In Ada 2012, if the subtype has a non-static predicate the full
+ -- In Ada 2012, if the subtype has a nonstatic predicate the full
-- range of the base type must be covered as well.
if Is_OK_Static_Subtype (Subtyp) then
@@ -1576,7 +3180,7 @@ package body Sem_Case is
end if;
-- Obtain static bounds of type, unless this is a generic formal
- -- discrete type for which all choices will be non-static.
+ -- discrete type for which all choices will be nonstatic.
if not Is_Generic_Type (Root_Type (Bounds_Type))
or else Ekind (Bounds_Type) /= E_Enumeration_Type
@@ -1638,7 +3242,7 @@ package body Sem_Case is
if Has_Predicates (E) then
- -- Use of non-static predicate is an error
+ -- Use of nonstatic predicate is an error
if not Is_Discrete_Type (E)
or else not Has_Static_Predicate (E)
@@ -1799,4 +3403,61 @@ package body Sem_Case is
end Generic_Check_Choices;
+ -----------------------------------------
+ -- Has_Static_Discriminant_Constraint --
+ -----------------------------------------
+
+ function Has_Static_Discriminant_Constraint
+ (Subtyp : Entity_Id) return Boolean
+ is
+ begin
+ if Has_Discriminants (Subtyp) and then Is_Constrained (Subtyp) then
+ declare
+ DC_Elmt : Elmt_Id := First_Elmt (Discriminant_Constraint (Subtyp));
+ begin
+ while Present (DC_Elmt) loop
+ if not All_Composite_Constraints_Static (Node (DC_Elmt)) then
+ return False;
+ end if;
+ Next_Elmt (DC_Elmt);
+ end loop;
+ return True;
+ end;
+ end if;
+ return False;
+ end Has_Static_Discriminant_Constraint;
+
+ ----------------------------
+ -- Is_Case_Choice_Pattern --
+ ----------------------------
+
+ function Is_Case_Choice_Pattern (Expr : Node_Id) return Boolean is
+ E : Node_Id := Expr;
+ begin
+ if not Extensions_Allowed then
+ return False;
+ end if;
+
+ loop
+ case Nkind (E) is
+ when N_Case_Statement_Alternative
+ | N_Case_Expression_Alternative
+ =>
+ -- We could return False if selecting expression is discrete,
+ -- but this doesn't seem to be worth the bother.
+ return True;
+
+ when N_Empty
+ | N_Statement_Other_Than_Procedure_Call
+ | N_Procedure_Call_Statement
+ | N_Declaration
+ =>
+ return False;
+
+ when others =>
+ E := Parent (E);
+ end case;
+ end loop;
+ end Is_Case_Choice_Pattern;
+
end Sem_Case;