]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[Ada] Add -gnatX support for casing on array values
authorSteve Baird <baird@adacore.com>
Fri, 9 Jul 2021 19:04:09 +0000 (12:04 -0700)
committerPierre-Marie de Rodat <derodat@adacore.com>
Wed, 22 Sep 2021 15:01:42 +0000 (15:01 +0000)
gcc/ada/

* exp_ch5.adb (Expand_General_Case_Statement.Pattern_Match): Add
new function Indexed_Element to handle array element
comparisons. Handle case choices that are array aggregates,
string literals, or names denoting constants.
* sem_case.adb (Composite_Case_Ops.Array_Case_Ops): New package
providing utilities needed for casing on arrays.
(Composite_Case_Ops.Choice_Analysis): If necessary, include
array length as a "component" (like a discriminant) when
traversing components. We do not (yet) partition choice analysis
to deal with unequal length choices separately. Instead, we
embed everything in the minimum-dimensionality Cartesian product
space needed to handle all choices properly; this is determined
by the length of the longest choice pattern.
(Composite_Case_Ops.Choice_Analysis.Traverse_Discrete_Parts):
Include length as a "component" in the traversal if necessary.
(Composite_Case_Ops.Choice_Analysis.Parse_Choice.Traverse_Choice):
Add support for case choices that are string literals or names
denoting constants.
(Composite_Case_Ops.Choice_Analysis): Include length as a
"component" in the analysis if necessary.
(Check_Choices.Check_Case_Pattern_Choices.Ops.Value_Sets.Value_Index_Count):
Improve error message when capacity exceeded.
* doc/gnat_rm/implementation_defined_pragmas.rst: Update
documentation to reflect current implementation status.
* gnat_rm.texi: Regenerate.

gcc/ada/doc/gnat_rm/implementation_defined_pragmas.rst
gcc/ada/exp_ch5.adb
gcc/ada/gnat_rm.texi
gcc/ada/sem_case.adb

index 6c81ca7db6160e65b339d8d7a1437186a23b4755..9d2f11305e897ea6821ab52455eed42fd614bcae 100644 (file)
@@ -2270,8 +2270,15 @@ of GNAT specific extensions are recognized as follows:
   values of the composite type shall be covered. The composite type of the
   selector shall be a nonlimited untagged (but possibly discriminated)
   record type, all of whose subcomponent subtypes are either static discrete
-  subtypes or record types that meet the same restrictions. Support for arrays
-  is planned, but not yet implemented.
+  subtypes or record types that meet the same restrictions.
+
+  Support for casing on arrays (and on records that contain arrays) is
+  currently subject to some restrictions. Non-positional
+  array aggregates are not supported as (or within) case choices. Likewise
+  for array type and subtype names. The current implementation exceeds
+  compile-time capacity limits in some annoyingly common scenarios; the
+  message generated in such cases is usually "Capacity exceeded in compiling
+  case statement with composite selector type".
 
   In addition, pattern bindings are supported. This is a mechanism
   for binding a name to a component of a matching value for use within
@@ -2280,7 +2287,8 @@ of GNAT specific extensions are recognized as follows:
   "is <identifier>". In the special case of a "box" component association,
   the identifier may instead be provided within the box. Either of these
   indicates that the given identifer denotes (a constant view of) the matching
-  subcomponent of the case selector.
+  subcomponent of the case selector. Binding is not yet supported for arrays
+  or subcomponents thereof.
 
   Consider this example (which uses type Rec from the previous example):
 
index 9827326f919cd51d65705a914674414ad06d19e8..21ac2a2b747ca1ad601d8096522a1ee752f0f1e8 100644 (file)
@@ -31,7 +31,6 @@ 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;
@@ -3365,6 +3364,30 @@ package body Exp_Ch5 is
               renames Pattern_Match;
             --  convenient rename for recursive calls
 
+            function Indexed_Element (Idx : Pos) return Node_Id;
+            --  Returns the Nth (well, ok, the Idxth) element of Object
+
+            ---------------------
+            -- Indexed_Element --
+            ---------------------
+
+            function Indexed_Element (Idx : Pos) return Node_Id is
+               Obj_Index : constant Node_Id :=
+                 Make_Op_Add (Loc,
+                   Left_Opnd =>
+                     Make_Attribute_Reference (Loc,
+                       Attribute_Name => Name_First,
+                       Prefix => New_Copy_Tree (Object)),
+                   Right_Opnd =>
+                     Make_Integer_Literal (Loc, Idx - 1));
+            begin
+               return Make_Indexed_Component (Loc,
+                        Prefix => New_Copy_Tree (Object),
+                        Expressions => New_List (Obj_Index));
+            end Indexed_Element;
+
+         --  Start of processing for Pattern_Match
+
          begin
             if Choice_Index /= 0 and not Suppress_Choice_Index_Update then
                pragma Assert (Present (Choice_Index_Decl));
@@ -3399,16 +3422,51 @@ package body Exp_Ch5 is
 
             case Nkind (Pattern) is
                when N_Aggregate =>
-                  return Result : Node_Id :=
-                    New_Occurrence_Of (Standard_True, Loc)
-                  do
+                  declare
+                     Result : Node_Id;
+                  begin
                      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;
+
+                        --  Nonpositional aggregates currently unimplemented.
+                        --  We flag that case during analysis, so an assertion
+                        --  is ok here.
+                        --
+                        pragma Assert
+                          (not Is_Non_Empty_List
+                                 (Component_Associations (Pattern)));
+
+                        declare
+                           Agg_Length : constant Node_Id :=
+                             Make_Integer_Literal (Loc,
+                               List_Length (Expressions (Pattern)));
+
+                           Obj_Length : constant Node_Id :=
+                             Make_Attribute_Reference (Loc,
+                               Attribute_Name => Name_Length,
+                               Prefix => New_Copy_Tree (Object));
+                        begin
+                           Result := Make_Op_Eq (Loc,
+                                       Left_Opnd  => Obj_Length,
+                                       Right_Opnd => Agg_Length);
+                        end;
+
+                        declare
+                           Expr : Node_Id := First (Expressions (Pattern));
+                           Idx  : Pos := 1;
+                        begin
+                           while Present (Expr) loop
+                              Result :=
+                                Make_And_Then (Loc,
+                                  Left_Opnd  => Result,
+                                  Right_Opnd =>
+                                    PM (Pattern => Expr,
+                                        Object => Indexed_Element (Idx)));
+                              Next (Expr);
+                              Idx := Idx + 1;
+                           end loop;
+                        end;
+
+                        return Result;
                      end if;
 
                      --  positional notation should have been normalized
@@ -3425,6 +3483,8 @@ package body Exp_Ch5 is
                              Selector_Name => New_Occurrence_Of
                                                 (Entity (Choice), Loc)));
                      begin
+                        Result := New_Occurrence_Of (Standard_True, Loc);
+
                         while Present (Component_Assoc) loop
                            Choice := First (Choices (Component_Assoc));
                            while Present (Choice) loop
@@ -3530,27 +3590,82 @@ package body Exp_Ch5 is
                            Next (Component_Assoc);
                         end loop;
                      end;
+                     return Result;
+                  end;
+
+               when N_String_Literal =>
+                  return Result : Node_Id do
+                     declare
+                        Char_Type : constant Entity_Id :=
+                          Root_Type (Component_Type (Etype (Pattern)));
+
+                        --  If the component type is not a standard character
+                        --  type then this string lit should have already been
+                        --  transformed into an aggregate in
+                        --  Resolve_String_Literal.
+                        --
+                        pragma Assert (Is_Standard_Character_Type (Char_Type));
+
+                        Str    : constant String_Id  := Strval (Pattern);
+                        Strlen : constant Nat        := String_Length (Str);
+
+                        Lit_Length : constant Node_Id :=
+                          Make_Integer_Literal (Loc, Strlen);
+
+                        Obj_Length : constant Node_Id :=
+                          Make_Attribute_Reference (Loc,
+                            Attribute_Name => Name_Length,
+                            Prefix => New_Copy_Tree (Object));
+                     begin
+                        Result := Make_Op_Eq (Loc,
+                                    Left_Opnd  => Obj_Length,
+                                    Right_Opnd => Lit_Length);
+
+                        for Idx in 1 .. Strlen loop
+                           declare
+                              C           : constant Char_Code :=
+                                Get_String_Char (Str, Idx);
+                              Obj_Element : constant Node_Id :=
+                                Indexed_Element (Idx);
+                              Char_Lit    : Node_Id;
+                           begin
+                              Set_Character_Literal_Name (C);
+                              Char_Lit :=
+                                Make_Character_Literal (Loc,
+                                  Chars              => Name_Find,
+                                  Char_Literal_Value => UI_From_CC (C));
+
+                              Result :=
+                                Make_And_Then (Loc,
+                                  Left_Opnd  => Result,
+                                  Right_Opnd =>
+                                    Make_Op_Eq (Loc,
+                                      Left_Opnd  => Obj_Element,
+                                      Right_Opnd => Char_Lit));
+                           end;
+                        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));
+                          Object  => New_Copy_Tree (Object)));
 
                when N_Identifier | N_Expanded_Name =>
                   if Is_Type (Entity (Pattern)) then
                      return Make_In (Loc,
-                       Left_Opnd  => Object,
+                       Left_Opnd  => New_Copy_Tree (Object),
                        Right_Opnd => New_Occurrence_Of
                                        (Entity (Pattern), Loc));
+                  elsif Ekind (Entity (Pattern)) = E_Constant then
+                     return PM (Pattern =>
+                                  Expression (Parent (Entity (Pattern))),
+                                Object => Object);
                   end if;
 
                when N_Others_Choice =>
index 349586edeadb7320b121005e9953b861c38d7ab7..08cef9fce3f4a927dd2d79ad66158293f6ae5e77 100644 (file)
@@ -21,7 +21,7 @@
 
 @copying
 @quotation
-GNAT Reference Manual , Jun 23, 2021
+GNAT Reference Manual , Aug 03, 2021
 
 AdaCore
 
@@ -3698,8 +3698,15 @@ will not be executed if the earlier alternative “matches”). All possible
 values of the composite type shall be covered. The composite type of the
 selector shall be a nonlimited untagged (but possibly discriminated)
 record type, all of whose subcomponent subtypes are either static discrete
-subtypes or record types that meet the same restrictions. Support for arrays
-is planned, but not yet implemented.
+subtypes or record types that meet the same restrictions.
+
+Support for casing on arrays (and on records that contain arrays) is
+currently subject to some restrictions. Non-positional
+array aggregates are not supported as (or within) case choices. Likewise
+for array type and subtype names. The current implementation exceeds
+compile-time capacity limits in some annoyingly common scenarios; the
+message generated in such cases is usually “Capacity exceeded in compiling
+case statement with composite selector type”.
 
 In addition, pattern bindings are supported. This is a mechanism
 for binding a name to a component of a matching value for use within
@@ -3708,7 +3715,8 @@ that occurs within a case choice, the expression may be followed by
 “is <identifier>”. In the special case of a “box” component association,
 the identifier may instead be provided within the box. Either of these
 indicates that the given identifer denotes (a constant view of) the matching
-subcomponent of the case selector.
+subcomponent of the case selector. Binding is not yet supported for arrays
+or subcomponents thereof.
 
 Consider this example (which uses type Rec from the previous example):
 
index 7d08da5af64c610e3bfd085c233d88ecbe8694ce..cc7e988226d0b86d2e8c09b7dea7a23c81010aed 100644 (file)
@@ -44,6 +44,7 @@ with Stand;          use Stand;
 with Sinfo;          use Sinfo;
 with Sinfo.Nodes;    use Sinfo.Nodes;
 with Sinfo.Utils;    use Sinfo.Utils;
+with Stringt;        use Stringt;
 with Table;
 with Tbuild;         use Tbuild;
 with Uintp;          use Uintp;
@@ -105,25 +106,70 @@ package body Sem_Case is
 
    package Composite_Case_Ops is
 
+      function Choice_Count (Alternatives : List_Id) return Nat;
+      --  The sum of the number of choices for each alternative in the given
+      --  list.
+
       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.
+      package Array_Case_Ops is
+         function Array_Choice_Length (Choice : Node_Id) return Nat;
+         --  Given a choice expression of an array type, returns its length.
+
+         function Normalized_Case_Expr_Type
+           (Case_Statement : Node_Id) return Entity_Id;
+         --  Usually returns the Etype of the selector expression of the
+         --  case statement. However, in the case of a constrained array
+         --  subtype with a nonstatic constraint, returns the unconstrained
+         --  array base type.
+
+         function Unconstrained_Array_Effective_Length
+           (Array_Type : Entity_Id; Case_Statement : Node_Id) return Nat;
+         --  If the nominal subtype of the case selector is unconstrained,
+         --  then use the length of the longest choice of the case statement.
+         --  Components beyond that index value will not influence the case
+         --  selection decision.
+
+         function Unconstrained_Array_Scalar_Part_Count
+           (Array_Type : Entity_Id; Case_Statement : Node_Id) return Nat;
+         --  Same as Scalar_Part_Count except that the value used for the
+         --  "length" of the array subtype being cased on is determined by
+         --  calling Unconstrained_Array_Effective_Length.
+      end Array_Case_Ops;
 
       generic
          Case_Statement : Node_Id;
       package Choice_Analysis is
 
+         use Array_Case_Ops;
+
          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));
+
+         Case_Expr_Type : constant Entity_Id :=
+           Normalized_Case_Expr_Type (Case_Statement);
+
+         Unconstrained_Array_Case : constant Boolean :=
+           Is_Array_Type (Case_Expr_Type)
+             and then not Is_Constrained (Case_Expr_Type);
+
+         --  If Unconstrained_Array_Case is True, choice lengths may differ:
+         --    when "Aaa" | "Bb" | "C" | "" =>
+         --
+         --  Strictly speaking, the name "Unconstrained_Array_Case" is
+         --  slightly imprecise; a subtype with a nonstatic constraint is
+         --  also treated as unconstrained (see Normalize_Case_Expr_Type).
+
          type Part_Id is new Int range
-           1 .. Scalar_Part_Count (Etype (Expression (Case_Statement)));
+           1 .. (if Unconstrained_Array_Case
+                 then Unconstrained_Array_Scalar_Part_Count
+                        (Case_Expr_Type, Case_Statement)
+                 else Scalar_Part_Count (Case_Expr_Type));
 
          type Discrete_Range_Info is
            record
@@ -1118,6 +1164,21 @@ package body Sem_Case is
          return UI_To_Int (Len);
       end Static_Array_Length;
 
+      ------------------
+      -- 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;
+
       -----------------------
       -- Scalar_Part_Count --
       -----------------------
@@ -1147,20 +1208,118 @@ package body Sem_Case is
          end if;
       end Scalar_Part_Count;
 
-      ------------------
-      -- Choice_Count --
-      ------------------
+      package body Array_Case_Ops is
 
-      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;
+         -------------------------
+         -- Array_Choice_Length --
+         -------------------------
+
+         function Array_Choice_Length (Choice : Node_Id) return Nat is
+         begin
+            case Nkind (Choice) is
+               when N_String_Literal =>
+                  return String_Length (Strval (Choice));
+               when N_Aggregate =>
+                  declare
+                     Bounds : constant Node_Id :=
+                       Aggregate_Bounds (Choice);
+                     pragma Assert (Is_OK_Static_Range (Bounds));
+                     Lo     : constant Uint :=
+                       Expr_Value (Low_Bound (Bounds));
+                     Hi     : constant Uint :=
+                       Expr_Value (High_Bound (Bounds));
+                     Len : constant Uint := (Hi - Lo) + 1;
+                  begin
+                     return UI_To_Int (Len);
+                  end;
+               when N_Has_Entity =>
+                  if Present (Entity (Choice))
+                    and then Ekind (Entity (Choice)) = E_Constant
+                  then
+                     return Array_Choice_Length
+                              (Expression (Parent (Entity (Choice))));
+                  end if;
+               when N_Others_Choice =>
+                  return 0;
+               when others =>
+                  null;
+            end case;
+
+            if Nkind (Original_Node (Choice))
+                 in N_String_Literal | N_Aggregate
+            then
+               return Array_Choice_Length (Original_Node (Choice));
+            end if;
+
+            Error_Msg_N ("Unsupported case choice", Choice);
+            return 0;
+         end Array_Choice_Length;
+
+         -------------------------------
+         -- Normalized_Case_Expr_Type --
+         -------------------------------
+
+         function Normalized_Case_Expr_Type
+           (Case_Statement : Node_Id) return Entity_Id
+         is
+            Unnormalized : constant Entity_Id :=
+              Etype (Expression (Case_Statement));
+         begin
+            if Is_Array_Type (Unnormalized)
+              and then Is_Constrained (Unnormalized)
+              and then not Has_Static_Array_Bounds (Unnormalized)
+            then
+               return Base_Type (Unnormalized);
+            else
+               return Unnormalized;
+            end if;
+         end Normalized_Case_Expr_Type;
+
+      ------------------------------------------
+         -- Unconstrained_Array_Effective_Length --
+      ------------------------------------------
+
+         function Unconstrained_Array_Effective_Length
+           (Array_Type : Entity_Id; Case_Statement : Node_Id) return Nat
+         is
+            pragma Assert (Is_Array_Type (Array_Type));
+            --  Array_Type is otherwise unreferenced for now.
+
+            Result : Nat := 0;
+            Alt : Node_Id := First (Alternatives (Case_Statement));
+         begin
+            while Present (Alt) loop
+               declare
+                  Choice : Node_Id := First (Discrete_Choices (Alt));
+               begin
+                  while Present (Choice) loop
+                     Result := Nat'Max (Result, Array_Choice_Length (Choice));
+                     Next (Choice);
+                  end loop;
+               end;
+               Next (Alt);
+            end loop;
+
+            return Result;
+         end Unconstrained_Array_Effective_Length;
+
+         -------------------------------------------
+         -- Unconstrained_Array_Scalar_Part_Count --
+         -------------------------------------------
+
+         function Unconstrained_Array_Scalar_Part_Count
+           (Array_Type : Entity_Id; Case_Statement : Node_Id) return Nat
+         is
+         begin
+            --  Add one for the length, which is treated like a discriminant
+
+            return 1 + (Unconstrained_Array_Effective_Length
+                          (Array_Type     => Array_Type,
+                           Case_Statement => Case_Statement)
+                        * Scalar_Part_Count (Component_Type (Array_Type)));
+         end Unconstrained_Array_Scalar_Part_Count;
+
+      end Array_Case_Ops;
 
       package body Choice_Analysis is
 
@@ -1220,9 +1379,32 @@ package body Sem_Case is
                     ((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;
+                  declare
+                     Len : Nat;
+                  begin
+                     if Is_Constrained (Subtyp) then
+                        Len := Static_Array_Length (Subtyp);
+                     else
+                        --  Length will be treated like a discriminant;
+                        --  We could compute High more precisely as
+                        --    1 + Index_Subtype'Last - Index_Subtype'First
+                        --  (we currently require that those bounds be
+                        --  static, so this is an option), but only downside of
+                        --  overshooting is if somebody wants to omit a
+                        --  "when others" choice and exhaustively cover all
+                        --  possibilities explicitly.
+                        Update_Result
+                          ((Low  => Uint_0,
+                            High => Uint_2 ** Uint_32));
+
+                        Len := Unconstrained_Array_Effective_Length
+                                 (Array_Type     => Subtyp,
+                                  Case_Statement => Case_Statement);
+                     end if;
+                     for I in 1 .. Len loop
+                        Traverse_Discrete_Parts (Component_Type (Subtyp));
+                     end loop;
+                  end;
                elsif Is_Record_Type (Subtyp) then
                   if Has_Static_Discriminant_Constraint (Subtyp) then
 
@@ -1274,7 +1456,7 @@ package body Sem_Case is
             end Traverse_Discrete_Parts;
 
          begin
-            Traverse_Discrete_Parts (Etype (Expression (Case_Statement)));
+            Traverse_Discrete_Parts (Case_Expr_Type);
             pragma Assert (Done or else Serious_Errors_Detected > 0);
             return Result;
          end Component_Bounds_Info;
@@ -1531,6 +1713,19 @@ package body Sem_Case is
                            & "choice not implemented", Expr);
                      end if;
 
+                     if not Unconstrained_Array_Case
+                        and then List_Length (Expressions (Expr))
+                           /= Nat (Part_Id'Last)
+                     then
+                        Error_Msg_N
+                          ("Array aggregate length"
+                            & List_Length (Expressions (Expr))'Image
+                            & " does not match length of"
+                            & " statically constrained case selector"
+                            & Part_Id'Last'Image, Expr);
+                        return;
+                     end if;
+
                      declare
                         Subexpr : Node_Id := First (Expressions (Expr));
                      begin
@@ -1542,9 +1737,50 @@ package body Sem_Case is
                   else
                      raise Program_Error;
                   end if;
+               elsif Nkind (Expr) = N_String_Literal then
+                  if not Is_Array_Type (Etype (Expr)) then
+                     Error_Msg_N
+                       ("User-defined string literal not allowed as/within"
+                        & "case choice", Expr);
+                  else
+                     declare
+                        Char_Type : constant Entity_Id :=
+                          Root_Type (Component_Type (Etype (Expr)));
+
+                        --  If the component type is not a standard character
+                        --  type then this string lit should have already been
+                        --  transformed into an aggregate in
+                        --  Resolve_String_Literal.
+                        --
+                        pragma Assert (Is_Standard_Character_Type (Char_Type));
+
+                        Str      : constant String_Id := Strval (Expr);
+                        Strlen   : constant Nat       := String_Length (Str);
+                        Char_Val : Uint;
+                     begin
+                        if not Unconstrained_Array_Case
+                           and then Strlen /= Nat (Part_Id'Last)
+                        then
+                           Error_Msg_N
+                             ("String literal length"
+                              & Strlen'Image
+                              & " does not match length of"
+                              & " statically constrained case selector"
+                              & Part_Id'Last'Image, Expr);
+                           return;
+                        end if;
+
+                        for Idx in 1 .. Strlen loop
+                           Char_Val :=
+                             UI_From_CC (Get_String_Char (Str, Idx));
+                           Update_Result ((Low | High => Char_Val));
+                        end loop;
+                     end;
+                  end if;
                elsif Is_Discrete_Type (Etype (Expr)) then
-                  if Nkind (Expr) in N_Has_Entity and then
-                    Is_Type (Entity (Expr))
+                  if Nkind (Expr) in N_Has_Entity
+                    and then Present (Entity (Expr))
+                    and then Is_Type (Entity (Expr))
                   then
                      declare
                         Low  : constant Node_Id :=
@@ -1559,10 +1795,20 @@ package body Sem_Case is
                      pragma Assert (Compile_Time_Known_Value (Expr));
                      Update_Result ((Low | High => Expr_Value (Expr)));
                   end if;
+               elsif Nkind (Expr) in N_Has_Entity
+                 and then Present (Entity (Expr))
+                 and then Ekind (Entity (Expr)) = E_Constant
+               then
+                  Traverse_Choice (Expression (Parent (Entity (Expr))));
+               elsif Nkind (Original_Node (Expr))
+                       in N_Aggregate | N_String_Literal
+               then
+                  Traverse_Choice (Original_Node (Expr));
                else
                   Error_Msg_N
-                    ("non-aggregate case choice subexpression which is not"
-                     & " of a discrete type not implemented", Expr);
+                    ("non-aggregate case choice (or subexpression thereof)"
+                     & " that is not of a discrete type not implemented",
+                     Expr);
                end if;
             end Traverse_Choice;
 
@@ -1572,8 +1818,26 @@ package body Sem_Case is
             if Nkind (Choice) = N_Others_Choice then
                return (Is_Others => True);
             end if;
+
+            if Unconstrained_Array_Case then
+               --  Treat length like a discriminant
+               Update_Result ((Low | High =>
+                                 UI_From_Int (Array_Choice_Length (Choice))));
+            end if;
+
             Traverse_Choice (Choice);
 
+            if Unconstrained_Array_Case then
+               --  This is somewhat tricky. Suppose we are casing on String,
+               --  the longest choice in the case statement is length 10, and
+               --  the choice we are looking at now is of length 6. We fill
+               --  in the trailing 4 slots here.
+               while Next_Part <= Part_Id'Last loop
+                  Update_Result_For_Full_Coverage
+                    (Comp_Type => Component_Type (Case_Expr_Type));
+               end loop;
+            end if;
+
             --  Avoid returning uninitialized garbage in error case
             if Next_Part /= Part_Id'Last + 1 then
                pragma Assert (Serious_Errors_Detected > 0);
@@ -2098,6 +2362,12 @@ package body Sem_Case is
                   Result := Result * Value_Index_Base (Uint_Sets.Size (Set));
                end loop;
                return Result;
+            exception
+               when Constraint_Error =>
+                  Error_Msg_N
+                    ("Capacity exceeded in compiling case statement with"
+                      & " composite selector type", Case_Statement);
+                  raise;
             end Value_Index_Count;
 
             Max_Value_Index : constant Value_Index_Base := Value_Index_Count;
@@ -3014,12 +3284,20 @@ package body Sem_Case is
                         "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_Constrained (Subtyp) then
+                     if not Is_Static_Subtype
+                              (Etype (First_Index (Subtyp)))
+                     then
+                        Error_Msg_N
+                          ("Unconstrained array subtype of case selector" &
+                           " has nonstatic index subtype", N);
+                     end if;
+
                   elsif not Is_OK_Static_Range (First_Index (Subtyp)) then
                      Error_Msg_N
                        ("array subtype of case selector (or " &
@@ -3077,10 +3355,6 @@ package body Sem_Case is
             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;