--  Evaluation of static expressions.
--  Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold
--
--  GHDL is free software; you can redistribute it and/or modify it under
--  the terms of the GNU General Public License as published by the Free
--  Software Foundation; either version 2, or (at your option) any later
--  version.
--
--  GHDL is distributed in the hope that it will be useful, but WITHOUT ANY
--  WARRANTY; without even the implied warranty of MERCHANTABILITY or
--  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
--  for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with GHDL; see the file COPYING.  If not, write to the Free
--  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
--  02111-1307, USA.
with Errorout; use Errorout;
with Name_Table; use Name_Table;
with Str_Table;
with Iirs_Utils; use Iirs_Utils;
with Std_Package; use Std_Package;
with Flags; use Flags;
with Std_Names;
with Ada.Characters.Handling;

package body Evaluation is
   function Get_Physical_Value (Expr : Iir) return Iir_Int64
   is
      pragma Unsuppress (Overflow_Check);
   begin
      case Get_Kind (Expr) is
         when Iir_Kind_Physical_Int_Literal =>
            return Get_Value (Expr)
              * Get_Value (Get_Physical_Unit_Value (Get_Unit_Name (Expr)));
         when Iir_Kind_Unit_Declaration =>
            return Get_Value (Get_Physical_Unit_Value (Expr));
         when others =>
            Error_Kind ("get_physical_value", Expr);
      end case;
   exception
      when Constraint_Error =>
         Error_Msg_Sem ("arithmetic overflow in physical expression", Expr);
         return Get_Value (Expr);
   end Get_Physical_Value;

   function Build_Integer (Val : Iir_Int64; Origin : Iir)
     return Iir_Integer_Literal
   is
      Res : Iir_Integer_Literal;
   begin
      Res := Create_Iir (Iir_Kind_Integer_Literal);
      Location_Copy (Res, Origin);
      Set_Value (Res, Val);
      Set_Type (Res, Get_Type (Origin));
      Set_Literal_Origin (Res, Origin);
      Set_Expr_Staticness (Res, Locally);
      return Res;
   end Build_Integer;

   function Build_Floating (Val : Iir_Fp64; Origin : Iir)
     return Iir_Floating_Point_Literal
   is
      Res : Iir_Floating_Point_Literal;
   begin
      Res := Create_Iir (Iir_Kind_Floating_Point_Literal);
      Location_Copy (Res, Origin);
      Set_Fp_Value (Res, Val);
      Set_Type (Res, Get_Type (Origin));
      Set_Literal_Origin (Res, Origin);
      Set_Expr_Staticness (Res, Locally);
      return Res;
   end Build_Floating;

   function Build_Enumeration (Val : Iir_Index32; Origin : Iir)
     return Iir_Enumeration_Literal
   is
      Res : Iir_Enumeration_Literal;
      Enum_Type : Iir;
      Enum_List : Iir_List;
      Lit : Iir_Enumeration_Literal;
   begin
      Enum_Type := Get_Base_Type (Get_Type (Origin));
      Enum_List := Get_Enumeration_Literal_List (Enum_Type);
      Lit := Get_Nth_Element (Enum_List, Integer (Val));

      Res := Create_Iir (Iir_Kind_Enumeration_Literal);
      Set_Identifier (Res, Get_Identifier (Lit));
      Location_Copy (Res, Origin);
      Set_Enum_Pos (Res, Iir_Int32 (Val));
      Set_Type (Res, Get_Type (Origin));
      Set_Literal_Origin (Res, Origin);
      Set_Expr_Staticness (Res, Locally);
      Set_Enumeration_Decl (Res, Lit);
      return Res;
   end Build_Enumeration;

   function Build_Boolean (Cond : Boolean; Origin : Iir) return Iir is
   begin
      return Build_Enumeration (Boolean'Pos (Cond), Origin);
   end Build_Boolean;

   function Build_Physical (Val : Iir_Int64; Origin : Iir)
     return Iir_Physical_Int_Literal
   is
      Res : Iir_Physical_Int_Literal;
   begin
      Res := Create_Iir (Iir_Kind_Physical_Int_Literal);
      Location_Copy (Res, Origin);
      Set_Unit_Name (Res, Get_Primary_Unit (Get_Type (Origin)));
      Set_Value (Res, Val);
      Set_Type (Res, Get_Type (Origin));
      Set_Literal_Origin (Res, Origin);
      Set_Expr_Staticness (Res, Locally);
      return Res;
   end Build_Physical;

   function Build_Discrete (Val : Iir_Int64; Origin : Iir)
     return Iir
   is
   begin
      case Get_Kind (Get_Type (Origin)) is
         when Iir_Kind_Enumeration_Type_Definition
           | Iir_Kind_Enumeration_Subtype_Definition =>
            return Build_Enumeration (Iir_Index32 (Val), Origin);
         when Iir_Kind_Integer_Type_Definition
           | Iir_Kind_Integer_Subtype_Definition =>
            return Build_Integer (Val, Origin);
         when others =>
            Error_Kind ("build_discrete", Get_Type (Origin));
      end case;
   end Build_Discrete;

   function Build_String (Val : String_Id; Len : Nat32; Origin : Iir)
     return Iir_String_Literal
   is
      Res : Iir_String_Literal;
   begin
      Res := Create_Iir (Iir_Kind_String_Literal);
      Location_Copy (Res, Origin);
      Set_String_Id (Res, Val);
      Set_String_Length (Res, Len);
      Set_Type (Res, Get_Type (Origin));
      Set_Literal_Origin (Res, Origin);
      Set_Expr_Staticness (Res, Locally);
      return Res;
   end Build_String;

   function Build_Simple_Aggregate
     (El_List : Iir_List; Origin : Iir; Stype : Iir)
     return Iir_Simple_Aggregate
   is
      Res : Iir_Simple_Aggregate;
   begin
      Res := Create_Iir (Iir_Kind_Simple_Aggregate);
      Location_Copy (Res, Origin);
      Set_Simple_Aggregate_List (Res, El_List);
      Set_Type (Res, Stype);
      Set_Literal_Origin (Res, Origin);
      Set_Expr_Staticness (Res, Locally);
      return Res;
   end Build_Simple_Aggregate;

   function Build_Constant (Val : Iir; Origin : Iir) return Iir
   is
      Res : Iir;
   begin
      --  Note: this must work for any literals, because it may be used to
      --  replace a locally static constant by its initial value.
      case Get_Kind (Val) is
         when Iir_Kind_Integer_Literal =>
            Res := Create_Iir (Iir_Kind_Integer_Literal);
            Set_Value (Res, Get_Value (Val));
         when Iir_Kind_Floating_Point_Literal =>
            Res := Create_Iir (Iir_Kind_Floating_Point_Literal);
            Set_Fp_Value (Res, Get_Fp_Value (Val));
         when Iir_Kind_Enumeration_Literal =>
            return Get_Nth_Element
              (Get_Enumeration_Literal_List
               (Get_Base_Type (Get_Type (Origin))),
               Integer (Get_Enum_Pos (Val)));
         when Iir_Kind_Physical_Int_Literal =>
            declare
               Prim : Iir;
            begin
               Res := Create_Iir (Iir_Kind_Physical_Int_Literal);
               Prim := Get_Primary_Unit (Get_Base_Type (Get_Type (Origin)));
               Set_Unit_Name (Res, Prim);
               if Get_Unit_Name (Val) = Prim then
                  Set_Value (Res, Get_Value (Val));
               else
                  raise Internal_Error;
                  --Set_Abstract_Literal (Res, Get_Abstract_Literal (Val)
                  --                      * Get_Value (Get_Name (Val)));
               end if;
            end;
         when Iir_Kind_Unit_Declaration =>
            Res := Create_Iir (Iir_Kind_Physical_Int_Literal);
            Set_Value (Res, Get_Physical_Value (Val));
            Set_Unit_Name (Res, Get_Primary_Unit (Get_Type (Val)));

         when Iir_Kind_String_Literal =>
            Res := Create_Iir (Iir_Kind_String_Literal);
            Set_String_Id (Res, Get_String_Id (Val));
            Set_String_Length (Res, Get_String_Length (Val));

         when Iir_Kind_Bit_String_Literal =>
            Res := Create_Iir (Iir_Kind_Bit_String_Literal);
            Set_String_Id (Res, Get_String_Id (Val));
            Set_String_Length (Res, Get_String_Length (Val));
            Set_Bit_String_Base (Res, Get_Bit_String_Base (Val));
            Set_Bit_String_0 (Res, Get_Bit_String_0 (Val));
            Set_Bit_String_1 (Res, Get_Bit_String_1 (Val));

         when Iir_Kind_Simple_Aggregate =>
            Res := Create_Iir (Iir_Kind_Simple_Aggregate);
            Set_Simple_Aggregate_List (Res, Get_Simple_Aggregate_List (Val));

         when Iir_Kind_Error =>
            return Val;

         when others =>
            Error_Kind ("build_constant", Val);
      end case;
      Location_Copy (Res, Origin);
      Set_Type (Res, Get_Type (Origin));
      Set_Literal_Origin (Res, Origin);
      Set_Expr_Staticness (Res, Locally);
      return Res;
   end Build_Constant;

   --  A_RANGE is a range expression, whose type, location, expr_staticness,
   --  left_limit and direction are set.
   --  Type of A_RANGE must have a range_constraint.
   --  Set the right limit of A_RANGE from LEN.
   procedure Set_Right_Limit_By_Length (A_Range : Iir; Len : Iir_Int64)
   is
      Left, Right : Iir;
      Pos : Iir_Int64;
      A_Type : Iir;
   begin
      if Get_Expr_Staticness (A_Range) /= Locally then
         raise Internal_Error;
      end if;
      A_Type := Get_Type (A_Range);

      Left := Get_Left_Limit (A_Range);

      Pos := Eval_Pos (Left);
      case Get_Direction (A_Range) is
         when Iir_To =>
            Pos := Pos + Len -1;
         when Iir_Downto =>
            Pos := Pos - Len + 1;
      end case;
      if Len > 0
        and then not Eval_Int_In_Range (Pos, Get_Range_Constraint (A_Type))
      then
         Error_Msg_Sem ("range length is beyond subtype length", A_Range);
         Right := Left;
      else
         -- FIXME: what about nul range?
         Right := Build_Discrete (Pos, A_Range);
         Set_Literal_Origin (Right, Null_Iir);
      end if;
      Set_Right_Limit (A_Range, Right);
   end Set_Right_Limit_By_Length;

   --  Create a range of type A_TYPE whose length is LEN.
   --  Note: only two nodes are created:
   --  * the range_expression (node returned)
   --  * the right bound
   --  The left bound *IS NOT* created, but points to the left bound of A_TYPE.
   function Create_Range_By_Length
     (A_Type : Iir; Len : Iir_Int64; Loc : Location_Type)
     return Iir
   is
      Index_Constraint : Iir;
      Constraint : Iir;
   begin
      --  The left limit must be locally static in order to compute the right
      --  limit.
      if Get_Type_Staticness (A_Type) /= Locally then
         raise Internal_Error;
      end if;

      Index_Constraint := Get_Range_Constraint (A_Type);
      Constraint := Create_Iir (Iir_Kind_Range_Expression);
      Set_Location (Constraint, Loc);
      Set_Expr_Staticness (Constraint, Locally);
      Set_Type (Constraint, A_Type);
      Set_Left_Limit (Constraint, Get_Left_Limit (Index_Constraint));
      Set_Direction (Constraint, Get_Direction (Index_Constraint));
      Set_Right_Limit_By_Length (Constraint, Len);
      return Constraint;
   end Create_Range_By_Length;

   function Create_Range_Subtype_From_Type (A_Type : Iir; Loc : Location_Type)
                                          return Iir
   is
      Res : Iir;
   begin
      if Get_Type_Staticness (A_Type) /= Locally then
         raise Internal_Error;
      end if;

      case Get_Kind (A_Type) is
         when Iir_Kind_Enumeration_Type_Definition =>
            Res := Create_Iir (Iir_Kind_Enumeration_Subtype_Definition);
         when Iir_Kind_Integer_Subtype_Definition
           | Iir_Kind_Enumeration_Subtype_Definition =>
            Res := Create_Iir (Get_Kind (A_Type));
         when others =>
            Error_Kind ("create_range_subtype_by_length", A_Type);
      end case;
      Set_Location (Res, Loc);
      Set_Base_Type (Res, Get_Base_Type (A_Type));
      Set_Type_Staticness (Res, Locally);

      return Res;
   end Create_Range_Subtype_From_Type;

   --  Create a subtype of A_TYPE whose length is LEN.
   --  This is used to create subtypes for strings or aggregates.
   function Create_Range_Subtype_By_Length
     (A_Type : Iir; Len : Iir_Int64; Loc : Location_Type)
     return Iir
   is
      Res : Iir;
   begin
      Res := Create_Range_Subtype_From_Type (A_Type, Loc);

      Set_Range_Constraint (Res, Create_Range_By_Length (A_Type, Len, Loc));
      return Res;
   end Create_Range_Subtype_By_Length;

   function Create_Unidim_Array_From_Index
     (Base_Type : Iir; Index_Type : Iir; Loc : Iir)
     return Iir_Array_Subtype_Definition
   is
      Res : Iir_Array_Subtype_Definition;
   begin
      Res := Create_Array_Subtype (Base_Type, Get_Location (Loc));
      Append_Element (Get_Index_Subtype_List (Res), Index_Type);
      Set_Type_Staticness (Res, Min (Get_Type_Staticness (Res),
                                     Get_Type_Staticness (Index_Type)));
      Set_Constraint_State (Res, Fully_Constrained);
      Set_Index_Constraint_Flag (Res, True);
      return Res;
   end Create_Unidim_Array_From_Index;

   function Create_Unidim_Array_By_Length
     (Base_Type : Iir; Len : Iir_Int64; Loc : Iir)
     return Iir_Array_Subtype_Definition
   is
      Index_Type : Iir;
      N_Index_Type : Iir;
   begin
      Index_Type := Get_First_Element (Get_Index_Subtype_List (Base_Type));
      N_Index_Type := Create_Range_Subtype_By_Length
        (Index_Type, Len, Get_Location (Loc));
      return Create_Unidim_Array_From_Index (Base_Type, N_Index_Type, Loc);
   end Create_Unidim_Array_By_Length;

   function Eval_String_Literal (Str : Iir) return Iir
   is
      Ptr : String_Fat_Acc;
      Len : Nat32;
   begin
      case Get_Kind (Str) is
         when Iir_Kind_String_Literal =>
            declare
               Element_Type : Iir;
               Literal_List : Iir_List;
               Lit : Iir;

               List : Iir_List;
            begin
               Element_Type := Get_Base_Type
                 (Get_Element_Subtype (Get_Base_Type (Get_Type (Str))));
               Literal_List := Get_Enumeration_Literal_List (Element_Type);
               List := Create_Iir_List;

               Ptr := Get_String_Fat_Acc (Str);
               Len := Get_String_Length (Str);

               for I in 1 .. Len loop
                  Lit := Find_Name_In_List
                    (Literal_List,
                     Name_Table.Get_Identifier (Ptr (I)));
                  Append_Element (List, Lit);
               end loop;
               return Build_Simple_Aggregate (List, Str, Get_Type (Str));
            end;
         when Iir_Kind_Bit_String_Literal =>
            declare
               Str_Type : Iir;
               List : Iir_List;
               Lit_0 : Iir;
               Lit_1 : Iir;
            begin
               Str_Type := Get_Type (Str);
               List := Create_Iir_List;
               Lit_0 := Get_Bit_String_0 (Str);
               Lit_1 := Get_Bit_String_1 (Str);

               Ptr := Get_String_Fat_Acc (Str);
               Len := Get_String_Length (Str);

               for I in 1 .. Len loop
                  case Ptr (I) is
                     when '0' =>
                        Append_Element (List, Lit_0);
                     when '1' =>
                        Append_Element (List, Lit_1);
                     when others =>
                        raise Internal_Error;
                  end case;
               end loop;
               return Build_Simple_Aggregate (List, Str, Str_Type);
            end;
         when Iir_Kind_Simple_Aggregate =>
            return Str;
         when others =>
            Error_Kind ("eval_string_literal", Str);
      end case;
   end Eval_String_Literal;

   function Eval_Monadic_Operator (Orig : Iir; Operand : Iir) return Iir
   is
      pragma Unsuppress (Overflow_Check);

      Func : Iir_Predefined_Functions;
   begin
      Func := Get_Implicit_Definition (Get_Implementation (Orig));
      case Func is
         when Iir_Predefined_Integer_Negation =>
            return Build_Integer (-Get_Value (Operand), Orig);
         when Iir_Predefined_Integer_Identity =>
            return Build_Integer (Get_Value (Operand), Orig);
         when Iir_Predefined_Integer_Absolute =>
            return Build_Integer (abs Get_Value (Operand), Orig);

         when Iir_Predefined_Floating_Negation =>
            return Build_Floating (-Get_Fp_Value (Operand), Orig);
         when Iir_Predefined_Floating_Identity =>
            return Build_Floating (Get_Fp_Value (Operand), Orig);
         when Iir_Predefined_Floating_Absolute =>
            return Build_Floating (abs Get_Fp_Value (Operand), Orig);

         when Iir_Predefined_Physical_Negation =>
            return Build_Physical (-Get_Physical_Value (Operand), Orig);
         when Iir_Predefined_Physical_Identity =>
            return Build_Physical (Get_Physical_Value (Operand), Orig);
         when Iir_Predefined_Physical_Absolute =>
            return Build_Physical (abs Get_Physical_Value (Operand), Orig);

         when Iir_Predefined_Boolean_Not
           | Iir_Predefined_Bit_Not =>
            return Build_Enumeration
              (Boolean'Pos (Get_Enum_Pos (Operand) = 0), Orig);

         when Iir_Predefined_Bit_Array_Not =>
            declare
               O_List : Iir_List;
               R_List : Iir_List;
               El : Iir;
               Lit : Iir;
            begin
               O_List := Get_Simple_Aggregate_List
                 (Eval_String_Literal (Operand));
               R_List := Create_Iir_List;

               for I in Natural loop
                  El := Get_Nth_Element (O_List, I);
                  exit when El = Null_Iir;
                  case Get_Enum_Pos (El) is
                     when 0 =>
                        Lit := Bit_1;
                     when 1 =>
                        Lit := Bit_0;
                     when others =>
                        raise Internal_Error;
                  end case;
                  Append_Element (R_List, Lit);
               end loop;
               return Build_Simple_Aggregate
                 (R_List, Orig, Get_Type (Operand));
            end;
         when others =>
            Error_Internal (Orig, "eval_monadic_operator: " &
                            Iir_Predefined_Functions'Image (Func));
      end case;
   exception
      when Constraint_Error =>
         Error_Msg_Sem ("arithmetic overflow in static expression", Orig);
         return Orig;
   end Eval_Monadic_Operator;

   function Eval_Dyadic_Bit_Array_Operator
     (Expr : Iir;
      Left, Right : Iir;
      Func : Iir_Predefined_Dyadic_Bit_Array_Functions)
     return Iir
   is
      use Str_Table;
      L_Str : constant String_Fat_Acc := Get_String_Fat_Acc (Left);
      R_Str : constant String_Fat_Acc := Get_String_Fat_Acc (Right);
      Len : Nat32;
      Id : String_Id;
   begin
      Len := Get_String_Length (Left);
      if Len /= Get_String_Length (Right) then
         Error_Msg_Sem ("length of left and right operands mismatch", Expr);
         return Left;
      else
         Id := Start;
         case Func is
            when Iir_Predefined_Bit_Array_And =>
               for I in 1 .. Len loop
                  case L_Str (I) is
                     when '0' =>
                        Append ('0');
                     when '1' =>
                        Append (R_Str (I));
                     when others =>
                        raise Internal_Error;
                  end case;
               end loop;
            when Iir_Predefined_Bit_Array_Nand =>
               for I in 1 .. Len loop
                  case L_Str (I) is
                     when '0' =>
                        Append ('1');
                     when '1' =>
                        case R_Str (I) is
                           when '0' =>
                              Append ('1');
                           when '1' =>
                              Append ('0');
                           when others =>
                              raise Internal_Error;
                        end case;
                     when others =>
                        raise Internal_Error;
                  end case;
               end loop;
            when Iir_Predefined_Bit_Array_Or =>
               for I in 1 .. Len loop
                  case L_Str (I) is
                     when '1' =>
                        Append ('1');
                     when '0' =>
                        Append (R_Str (I));
                     when others =>
                        raise Internal_Error;
                  end case;
               end loop;
            when Iir_Predefined_Bit_Array_Nor =>
               for I in 1 .. Len loop
                  case L_Str (I) is
                     when '1' =>
                        Append ('0');
                     when '0' =>
                        case R_Str (I) is
                           when '0' =>
                              Append ('1');
                           when '1' =>
                              Append ('0');
                           when others =>
                              raise Internal_Error;
                        end case;
                     when others =>
                        raise Internal_Error;
                  end case;
               end loop;
            when Iir_Predefined_Bit_Array_Xor =>
               for I in 1 .. Len loop
                  case L_Str (I) is
                     when '1' =>
                        case R_Str (I) is
                           when '0' =>
                              Append ('1');
                           when '1' =>
                              Append ('0');
                           when others =>
                              raise Internal_Error;
                        end case;
                     when '0' =>
                        case R_Str (I) is
                           when '0' =>
                              Append ('0');
                           when '1' =>
                              Append ('1');
                           when others =>
                              raise Internal_Error;
                        end case;
                     when others =>
                        raise Internal_Error;
                  end case;
               end loop;
            when others =>
               Error_Internal (Expr, "eval_dyadic_bit_array_functions: " &
                               Iir_Predefined_Functions'Image (Func));
         end case;
         Finish;
         return Build_String (Id, Len, Left);
      end if;
   end Eval_Dyadic_Bit_Array_Operator;

   --  Return TRUE if VAL /= 0.
   function Check_Integer_Division_By_Zero (Expr : Iir; Val : Iir)
                                           return Boolean
   is
   begin
      if Get_Value (Val) = 0 then
         Error_Msg_Sem ("division by 0", Expr);
         return False;
      else
         return True;
      end if;
   end Check_Integer_Division_By_Zero;

   function Eval_Shift_Operator
     (Left, Right : Iir; Origin : Iir; Func : Iir_Predefined_Shift_Functions)
     return Iir
   is
      Count : Iir_Int64;
      Cnt : Natural;
      Len : Natural;
      Arr_List : Iir_List;
      Res_List : Iir_List;
      Dir_Left : Boolean;
      E : Iir;
   begin
      Count := Get_Value (Right);
      Arr_List := Get_Simple_Aggregate_List (Left);
      Len := Get_Nbr_Elements (Arr_List);
      --  LRM93 7.2.3
      --  That is, if R is 0 or if L is a null array, the return value is L.
      if Count = 0 or Len = 0 then
         return Build_Simple_Aggregate (Arr_List, Origin, Get_Type (Left));
      end if;
      case Func is
         when Iir_Predefined_Array_Sll
           | Iir_Predefined_Array_Sla
           | Iir_Predefined_Array_Rol =>
            Dir_Left := True;
         when Iir_Predefined_Array_Srl
           | Iir_Predefined_Array_Sra
           | Iir_Predefined_Array_Ror =>
            Dir_Left := False;
      end case;
      if Count < 0 then
         Cnt := Natural (-Count);
         Dir_Left := not Dir_Left;
      else
         Cnt := Natural (Count);
      end if;

      case Func is
         when Iir_Predefined_Array_Sll
           | Iir_Predefined_Array_Srl =>
            declare
               Enum_List : Iir_List;
            begin
               Enum_List := Get_Enumeration_Literal_List
                 (Get_Base_Type (Get_Element_Subtype (Get_Type (Left))));
               E := Get_Nth_Element (Enum_List, 0);
            end;
         when Iir_Predefined_Array_Sla
           | Iir_Predefined_Array_Sra =>
            if Dir_Left then
               E := Get_Nth_Element (Arr_List, Len - 1);
            else
               E := Get_Nth_Element (Arr_List, 0);
            end if;
         when Iir_Predefined_Array_Rol
           | Iir_Predefined_Array_Ror =>
            Cnt := Cnt mod Len;
            if not Dir_Left then
               Cnt := (Len - Cnt) mod Len;
            end if;
      end case;

      Res_List := Create_Iir_List;

      case Func is
         when Iir_Predefined_Array_Sll
           | Iir_Predefined_Array_Srl
           | Iir_Predefined_Array_Sla
           | Iir_Predefined_Array_Sra =>
            if Dir_Left then
               if Cnt < Len then
                  for I in Cnt .. Len - 1 loop
                     Append_Element
                       (Res_List, Get_Nth_Element (Arr_List, I));
                  end loop;
               else
                  Cnt := Len;
               end if;
               for I in 0 .. Cnt - 1 loop
                  Append_Element (Res_List, E);
               end loop;
            else
               if Cnt > Len then
                  Cnt := Len;
               end if;
               for I in 0 .. Cnt - 1 loop
                  Append_Element (Res_List, E);
               end loop;
               for I in Cnt .. Len - 1 loop
                  Append_Element
                    (Res_List, Get_Nth_Element (Arr_List, I - Cnt));
               end loop;
            end if;
         when Iir_Predefined_Array_Rol
           | Iir_Predefined_Array_Ror =>
            for I in 1 .. Len loop
               Append_Element
                 (Res_List, Get_Nth_Element (Arr_List, Cnt));
               Cnt := Cnt + 1;
               if Cnt = Len then
                  Cnt := 0;
               end if;
            end loop;
      end case;
      return Build_Simple_Aggregate (Res_List, Origin, Get_Type (Left));
   end Eval_Shift_Operator;

   --  Note: operands must be locally static.
   function Eval_Concatenation
     (Left, Right : Iir; Orig : Iir; Func : Iir_Predefined_Concat_Functions)
     return Iir
   is
      Res_List : Iir_List;
      L : Natural;
      Res_Type : Iir;
      Origin_Type : Iir;
      Left_List, Right_List : Iir_List;
   begin
      Res_List := Create_Iir_List;
      --  Do the concatenation.
      --  Left:
      case Func is
         when Iir_Predefined_Element_Array_Concat
           | Iir_Predefined_Element_Element_Concat =>
            Append_Element (Res_List, Left);
         when Iir_Predefined_Array_Element_Concat
           | Iir_Predefined_Array_Array_Concat =>
            Left_List :=
              Get_Simple_Aggregate_List (Eval_String_Literal (Left));
            L := Get_Nbr_Elements (Left_List);
            for I in 0 .. L - 1 loop
               Append_Element (Res_List, Get_Nth_Element (Left_List, I));
            end loop;
      end case;
      --  Right:
      case Func is
         when Iir_Predefined_Array_Element_Concat
           | Iir_Predefined_Element_Element_Concat =>
            Append_Element (Res_List, Right);
         when Iir_Predefined_Element_Array_Concat
           | Iir_Predefined_Array_Array_Concat =>
            Right_List :=
              Get_Simple_Aggregate_List (Eval_String_Literal (Right));
            L := Get_Nbr_Elements (Right_List);
            for I in 0 .. L - 1 loop
               Append_Element (Res_List, Get_Nth_Element (Right_List, I));
            end loop;
      end case;
      L := Get_Nbr_Elements (Res_List);

      --  Compute subtype...
      Origin_Type := Get_Type (Orig);
      Res_Type := Null_Iir;
      if Func = Iir_Predefined_Array_Array_Concat
        and then Get_Nbr_Elements (Left_List) = 0
      then
         if Flags.Vhdl_Std = Vhdl_87 then
            --  LRM87 7.2.4
            --  [...], unless the left operand is a null array, in which case
            --  the result of the concatenation is the right operand.
            Res_Type := Get_Type (Right);
         else
            --  LRM93 7.2.4
            --  If both operands are null arrays, then the result of the
            --  concatenation is the right operand.
            if Get_Nbr_Elements (Right_List) = 0 then
               Res_Type := Get_Type (Right);
            end if;
         end if;
      end if;
      if Res_Type = Null_Iir then
         if Flags.Vhdl_Std = Vhdl_87
           and then (Func = Iir_Predefined_Array_Array_Concat
                     or Func = Iir_Predefined_Array_Element_Concat)
         then
            --  LRM87 7.2.4
            --  The left bound of the result is the left operand, [...]
            --
            --  LRM87 7.2.4
            --  The direction of the result is the direction of the left
            --  operand, [...]
            declare
               A_Range : Iir;
               Left_Index : Iir;
               Left_Range : Iir;
               Index_Type : Iir;
               Ret_Type : Iir;
            begin
               Left_Index := Get_Nth_Element
                 (Get_Index_Subtype_List (Get_Type (Left)), 0);
               Left_Range := Get_Range_Constraint (Left_Index);

               A_Range := Create_Iir (Iir_Kind_Range_Expression);
               Ret_Type := Get_Return_Type (Get_Implementation (Orig));
               Set_Type
                 (A_Range,
                  Get_First_Element (Get_Index_Subtype_List (Ret_Type)));
               Set_Expr_Staticness (A_Range, Locally);
               Set_Left_Limit (A_Range, Get_Left_Limit (Left_Range));
               Set_Direction (A_Range, Get_Direction (Left_Range));
               Location_Copy (A_Range, Orig);
               Set_Right_Limit_By_Length (A_Range, Iir_Int64 (L));
               Index_Type := Create_Range_Subtype_From_Type
                 (Left_Index, Get_Location (Orig));
               Set_Range_Constraint (Index_Type, A_Range);
               Res_Type := Create_Unidim_Array_From_Index
                 (Origin_Type, Index_Type, Orig);
            end;
         else
            --  LRM93 7.2.4
            --  Otherwise, the direction and bounds of the result are
            --  determined as follows: let S be the index subtype of the base
            --  type of the result.  The direction of the result of the
            --  concatenation is the direction of S, and the left bound of the
            --  result is S'LEFT.
            Res_Type := Create_Unidim_Array_By_Length
              (Origin_Type, Iir_Int64 (L), Orig);
         end if;
      end if;
      --  FIXME: this is not necessarily a string, it may be an aggregate if
      --  element type is not a character type.
      return Build_Simple_Aggregate (Res_List, Orig, Res_Type);
   end Eval_Concatenation;

   function Eval_Array_Equality (Left, Right : Iir) return Boolean
   is
      L_List : Iir_List;
      R_List : Iir_List;
      N : Natural;
   begin
      --  FIXME: the simple aggregates are lost.
      L_List := Get_Simple_Aggregate_List (Eval_String_Literal (Left));
      R_List := Get_Simple_Aggregate_List (Eval_String_Literal (Right));
      N := Get_Nbr_Elements (L_List);
      if N /= Get_Nbr_Elements (R_List) then
         return False;
      end if;
      for I in 0 .. N - 1 loop
         --  FIXME: this is wrong: (eg: evaluated lit)
         if Get_Nth_Element (L_List, I) /= Get_Nth_Element (R_List, I) then
            return False;
         end if;
      end loop;
      return True;
   end Eval_Array_Equality;

   --  ORIG is either a dyadic operator or a function call.
   function Eval_Dyadic_Operator (Orig : Iir; Left, Right : Iir)
     return Iir
   is
      pragma Unsuppress (Overflow_Check);
      Func : Iir_Predefined_Functions;
   begin
      if Get_Kind (Left) = Iir_Kind_Error
        or else Get_Kind (Right) = Iir_Kind_Error
      then
         return Create_Error_Expr (Orig, Get_Type (Orig));
      end if;

      Func := Get_Implicit_Definition (Get_Implementation (Orig));
      case Func is
         when Iir_Predefined_Integer_Plus =>
            return Build_Integer (Get_Value (Left) + Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Minus =>
            return Build_Integer (Get_Value (Left) - Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Mul =>
            return Build_Integer (Get_Value (Left) * Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Div =>
            if Check_Integer_Division_By_Zero (Orig, Right) then
               return Build_Integer
                 (Get_Value (Left) / Get_Value (Right), Orig);
            else
               return Null_Iir;
            end if;
         when Iir_Predefined_Integer_Mod =>
            if Check_Integer_Division_By_Zero (Orig, Right) then
               return Build_Integer
                 (Get_Value (Left) mod Get_Value (Right), Orig);
            else
               return Null_Iir;
            end if;
         when Iir_Predefined_Integer_Rem =>
            if Check_Integer_Division_By_Zero (Orig, Right) then
               return Build_Integer
                 (Get_Value (Left) rem Get_Value (Right), Orig);
            else
               return Null_Iir;
            end if;
         when Iir_Predefined_Integer_Exp =>
            return Build_Integer
              (Get_Value (Left) ** Integer (Get_Value (Right)), Orig);

         when Iir_Predefined_Integer_Equality =>
            return Build_Boolean (Get_Value (Left) = Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Inequality =>
            return Build_Boolean (Get_Value (Left) /= Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Greater_Equal =>
            return Build_Boolean (Get_Value (Left) >= Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Greater =>
            return Build_Boolean (Get_Value (Left) > Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Less_Equal =>
            return Build_Boolean (Get_Value (Left) <= Get_Value (Right), Orig);
         when Iir_Predefined_Integer_Less =>
            return Build_Boolean (Get_Value (Left) < Get_Value (Right), Orig);

         when Iir_Predefined_Integer_Minimum =>
            return Build_Integer
              (Iir_Int64'Min (Get_Value (Left), Get_Value (Right)), Orig);
         when Iir_Predefined_Integer_Maximum =>
            return Build_Integer
              (Iir_Int64'Max (Get_Value (Left), Get_Value (Right)), Orig);

         when Iir_Predefined_Floating_Equality =>
            return Build_Boolean
              (Get_Fp_Value (Left) = Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Inequality =>
            return Build_Boolean
              (Get_Fp_Value (Left) /= Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Greater =>
            return Build_Boolean
              (Get_Fp_Value (Left) > Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Greater_Equal =>
            return Build_Boolean
              (Get_Fp_Value (Left) >= Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Less =>
            return Build_Boolean
              (Get_Fp_Value (Left) < Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Less_Equal =>
            return Build_Boolean
              (Get_Fp_Value (Left) <= Get_Fp_Value (Right), Orig);

         when Iir_Predefined_Floating_Minus =>
            return Build_Floating
              (Get_Fp_Value (Left) - Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Plus =>
            return Build_Floating
              (Get_Fp_Value (Left) + Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Mul =>
            return Build_Floating
              (Get_Fp_Value (Left) * Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Floating_Div =>
            if Get_Fp_Value (Right) = 0.0 then
               Error_Msg_Sem ("right operand of division is 0", Orig);
               return Build_Floating (0.0, Orig);
            else
               return Build_Floating
                 (Get_Fp_Value (Left) / Get_Fp_Value (Right), Orig);
            end if;
         when Iir_Predefined_Floating_Exp =>
            declare
               Exp : Iir_Int64;
               Res : Iir_Fp64;
               Val : Iir_Fp64;
            begin
               Res := 1.0;
               Val := Get_Fp_Value (Left);
               Exp := abs Get_Value (Right);
               while Exp /= 0 loop
                  if Exp mod 2 = 1 then
                     Res := Res * Val;
                  end if;
                  Exp := Exp / 2;
                  Val := Val * Val;
               end loop;
               if Get_Value (Right) < 0 then
                  Res := 1.0 / Res;
               end if;
               return Build_Floating (Res, Orig);
            end;

         when Iir_Predefined_Floating_Minimum =>
            return Build_Floating
              (Iir_Fp64'Min (Get_Fp_Value (Left), Get_Fp_Value (Right)), Orig);
         when Iir_Predefined_Floating_Maximum =>
            return Build_Floating
              (Iir_Fp64'Max (Get_Fp_Value (Left), Get_Fp_Value (Right)), Orig);

         when Iir_Predefined_Physical_Equality =>
            return Build_Boolean
              (Get_Physical_Value (Left) = Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Inequality =>
            return Build_Boolean
              (Get_Physical_Value (Left) /= Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Greater_Equal =>
            return Build_Boolean
              (Get_Physical_Value (Left) >= Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Greater =>
            return Build_Boolean
              (Get_Physical_Value (Left) > Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Less_Equal =>
            return Build_Boolean
              (Get_Physical_Value (Left) <= Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Less =>
            return Build_Boolean
              (Get_Physical_Value (Left) < Get_Physical_Value (Right), Orig);

         when Iir_Predefined_Physical_Physical_Div =>
            return Build_Integer
              (Get_Physical_Value (Left) / Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Integer_Div =>
            return Build_Physical
              (Get_Physical_Value (Left) / Get_Value (Right), Orig);
         when Iir_Predefined_Physical_Minus =>
            return Build_Physical
              (Get_Physical_Value (Left) - Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Plus =>
            return Build_Physical
              (Get_Physical_Value (Left) + Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Integer_Physical_Mul =>
            return Build_Physical
              (Get_Value (Left) * Get_Physical_Value (Right), Orig);
         when Iir_Predefined_Physical_Integer_Mul =>
            return Build_Physical
              (Get_Physical_Value (Left) * Get_Value (Right), Orig);
         when Iir_Predefined_Real_Physical_Mul =>
            --  FIXME: overflow??
            return Build_Physical
              (Iir_Int64 (Get_Fp_Value (Left)
                          * Iir_Fp64 (Get_Physical_Value (Right))), Orig);
         when Iir_Predefined_Physical_Real_Mul =>
            --  FIXME: overflow??
            return Build_Physical
              (Iir_Int64 (Iir_Fp64 (Get_Physical_Value (Left))
                          * Get_Fp_Value (Right)), Orig);
         when Iir_Predefined_Physical_Real_Div =>
            --  FIXME: overflow??
            return Build_Physical
              (Iir_Int64 (Iir_Fp64 (Get_Physical_Value (Left))
                          / Get_Fp_Value (Right)), Orig);

         when Iir_Predefined_Physical_Minimum =>
            return Build_Physical (Iir_Int64'Min (Get_Physical_Value (Left),
                                                  Get_Physical_Value (Right)),
                                   Orig);
         when Iir_Predefined_Physical_Maximum =>
            return Build_Physical (Iir_Int64'Max (Get_Physical_Value (Left),
                                                  Get_Physical_Value (Right)),
                                   Orig);

         when Iir_Predefined_Element_Array_Concat
           | Iir_Predefined_Array_Element_Concat
           | Iir_Predefined_Array_Array_Concat
           | Iir_Predefined_Element_Element_Concat =>
            return Eval_Concatenation (Left, Right, Orig, Func);

         when Iir_Predefined_Enum_Equality
           | Iir_Predefined_Bit_Match_Equality =>
            return Build_Boolean
              (Get_Enum_Pos (Left) = Get_Enum_Pos (Right), Orig);
         when Iir_Predefined_Enum_Inequality
           | Iir_Predefined_Bit_Match_Inequality =>
            return Build_Boolean
              (Get_Enum_Pos (Left) /= Get_Enum_Pos (Right), Orig);
         when Iir_Predefined_Enum_Greater_Equal
           | Iir_Predefined_Bit_Match_Greater_Equal =>
            return Build_Boolean
              (Get_Enum_Pos (Left) >= Get_Enum_Pos (Right), Orig);
         when Iir_Predefined_Enum_Greater
           | Iir_Predefined_Bit_Match_Greater =>
            return Build_Boolean
              (Get_Enum_Pos (Left) > Get_Enum_Pos (Right), Orig);
         when Iir_Predefined_Enum_Less_Equal
           | Iir_Predefined_Bit_Match_Less_Equal =>
            return Build_Boolean
              (Get_Enum_Pos (Left) <= Get_Enum_Pos (Right), Orig);
         when Iir_Predefined_Enum_Less
           | Iir_Predefined_Bit_Match_Less =>
            return Build_Boolean
              (Get_Enum_Pos (Left) < Get_Enum_Pos (Right), Orig);

         when Iir_Predefined_Enum_Minimum =>
            return Build_Enumeration
              (Iir_Index32 (Iir_Int32'Min (Get_Enum_Pos (Left),
                                           Get_Enum_Pos (Right))),
               Orig);
         when Iir_Predefined_Enum_Maximum =>
            return Build_Enumeration
              (Iir_Index32 (Iir_Int32'Max (Get_Enum_Pos (Left),
                                           Get_Enum_Pos (Right))),
               Orig);

         when Iir_Predefined_Boolean_And
           | Iir_Predefined_Bit_And =>
            return Build_Boolean
              (Get_Enum_Pos (Left) = 1 and Get_Enum_Pos (Right) = 1, Orig);
         when Iir_Predefined_Boolean_Nand
           | Iir_Predefined_Bit_Nand =>
            return Build_Boolean
              (not (Get_Enum_Pos (Left) = 1 and Get_Enum_Pos (Right) = 1),
               Orig);
         when Iir_Predefined_Boolean_Or
           | Iir_Predefined_Bit_Or =>
            return Build_Boolean
              (Get_Enum_Pos (Left) = 1 or Get_Enum_Pos (Right) = 1, Orig);
         when Iir_Predefined_Boolean_Nor
           | Iir_Predefined_Bit_Nor =>
            return Build_Boolean
              (not (Get_Enum_Pos (Left) = 1 or Get_Enum_Pos (Right) = 1),
               Orig);
         when Iir_Predefined_Boolean_Xor
           | Iir_Predefined_Bit_Xor =>
            return Build_Boolean
              (Get_Enum_Pos (Left) = 1 xor Get_Enum_Pos (Right) = 1, Orig);
         when Iir_Predefined_Boolean_Xnor
           | Iir_Predefined_Bit_Xnor =>
            return Build_Boolean
              (not (Get_Enum_Pos (Left) = 1 xor Get_Enum_Pos (Right) = 1),
               Orig);

         when Iir_Predefined_Dyadic_Bit_Array_Functions =>
            return Eval_Dyadic_Bit_Array_Operator (Orig, Left, Right, Func);

         when Iir_Predefined_Universal_R_I_Mul =>
            return Build_Floating
              (Get_Fp_Value (Left) * Iir_Fp64 (Get_Value (Right)), Orig);
         when Iir_Predefined_Universal_I_R_Mul =>
            return Build_Floating
              (Iir_Fp64 (Get_Value (Left)) * Get_Fp_Value (Right), Orig);
         when Iir_Predefined_Universal_R_I_Div =>
            return Build_Floating
              (Get_Fp_Value (Left) / Iir_Fp64 (Get_Value (Right)), Orig);

         when Iir_Predefined_Array_Equality =>
            return Build_Boolean (Eval_Array_Equality (Left, Right), Orig);

         when Iir_Predefined_Array_Inequality =>
            return Build_Boolean (not Eval_Array_Equality (Left, Right), Orig);

         when Iir_Predefined_Array_Sll
           | Iir_Predefined_Array_Srl
           | Iir_Predefined_Array_Sla
           | Iir_Predefined_Array_Sra
           | Iir_Predefined_Array_Rol
           | Iir_Predefined_Array_Ror =>
            return Eval_Shift_Operator
              (Eval_String_Literal (Left), Right, Orig, Func);

         when Iir_Predefined_Array_Less
           | Iir_Predefined_Array_Less_Equal
           | Iir_Predefined_Array_Greater
           | Iir_Predefined_Array_Greater_Equal
           | Iir_Predefined_Boolean_Array_And
           | Iir_Predefined_Boolean_Array_Nand
           | Iir_Predefined_Boolean_Array_Or
           | Iir_Predefined_Boolean_Array_Nor
           | Iir_Predefined_Boolean_Array_Xor
           | Iir_Predefined_Boolean_Array_Xnor =>
            --  FIXME: todo.
            Error_Internal (Orig, "eval_dyadic_operator: " &
                            Iir_Predefined_Functions'Image (Func));

         when Iir_Predefined_Boolean_Not
           | Iir_Predefined_Boolean_Rising_Edge
           | Iir_Predefined_Boolean_Falling_Edge
           | Iir_Predefined_Bit_Not
           | Iir_Predefined_Bit_Rising_Edge
           | Iir_Predefined_Bit_Falling_Edge
           | Iir_Predefined_Integer_Absolute
           | Iir_Predefined_Integer_Identity
           | Iir_Predefined_Integer_Negation
           | Iir_Predefined_Floating_Absolute
           | Iir_Predefined_Floating_Negation
           | Iir_Predefined_Floating_Identity
           | Iir_Predefined_Physical_Absolute
           | Iir_Predefined_Physical_Identity
           | Iir_Predefined_Physical_Negation
           | Iir_Predefined_Error
           | Iir_Predefined_Record_Equality
           | Iir_Predefined_Record_Inequality
           | Iir_Predefined_Access_Equality
           | Iir_Predefined_Access_Inequality
           | Iir_Predefined_Bit_Array_Not
           | Iir_Predefined_Boolean_Array_Not
           | Iir_Predefined_Now_Function
           | Iir_Predefined_Deallocate
           | Iir_Predefined_Write
           | Iir_Predefined_Read
           | Iir_Predefined_Read_Length
           | Iir_Predefined_Flush
           | Iir_Predefined_File_Open
           | Iir_Predefined_File_Open_Status
           | Iir_Predefined_File_Close
           | Iir_Predefined_Endfile
           | Iir_Predefined_Attribute_Image
           | Iir_Predefined_Attribute_Value
           | Iir_Predefined_Attribute_Pos
           | Iir_Predefined_Attribute_Val
           | Iir_Predefined_Attribute_Succ
           | Iir_Predefined_Attribute_Pred
           | Iir_Predefined_Attribute_Rightof
           | Iir_Predefined_Attribute_Leftof
           | Iir_Predefined_Attribute_Left
           | Iir_Predefined_Attribute_Right
           | Iir_Predefined_Attribute_Event
           | Iir_Predefined_Attribute_Active
           | Iir_Predefined_Attribute_Last_Value
           | Iir_Predefined_Attribute_Last_Event
           | Iir_Predefined_Attribute_Last_Active
           | Iir_Predefined_Attribute_Driving
           | Iir_Predefined_Attribute_Driving_Value
           | Iir_Predefined_Array_To_String =>
            --  Not binary or never locally static.
            Error_Internal (Orig, "eval_dyadic_operator: " &
                              Iir_Predefined_Functions'Image (Func));

         when Iir_Predefined_Std_Ulogic_Match_Equality
           | Iir_Predefined_Std_Ulogic_Match_Inequality
           | Iir_Predefined_Std_Ulogic_Match_Less
           | Iir_Predefined_Std_Ulogic_Match_Less_Equal
           | Iir_Predefined_Std_Ulogic_Match_Greater
           | Iir_Predefined_Std_Ulogic_Match_Greater_Equal =>
            -- TODO
            raise Program_Error;
      end case;
   exception
      when Constraint_Error =>
         Error_Msg_Sem ("arithmetic overflow in static expression", Orig);
         return Null_Iir;
   end Eval_Dyadic_Operator;

   --  Evaluate any array attribute, return the type for the prefix.
   function Eval_Array_Attribute (Attr : Iir) return Iir
   is
      Prefix : Iir;
      Prefix_Type : Iir;
   begin
      Prefix := Get_Prefix (Attr);
      case Get_Kind (Prefix) is
         when Iir_Kinds_Object_Declaration
           | Iir_Kind_Selected_Element
           | Iir_Kind_Indexed_Name
           | Iir_Kind_Slice_Name
           | Iir_Kind_Subtype_Declaration
           | Iir_Kind_Type_Declaration
           | Iir_Kind_Implicit_Dereference =>
            Prefix_Type := Get_Type (Prefix);
         when Iir_Kind_Attribute_Value =>
            --  The type of the attribute declaration may be unconstrained.
            Prefix_Type := Get_Type
              (Get_Expression (Get_Attribute_Specification (Prefix)));
         when Iir_Kinds_Subtype_Definition =>
            Prefix_Type := Prefix;
         when others =>
            Error_Kind ("eval_array_attribute", Prefix);
      end case;
      if Get_Kind (Prefix_Type) /= Iir_Kind_Array_Subtype_Definition then
         Error_Kind ("eval_array_attribute(2)", Prefix_Type);
      end if;
      return Get_Nth_Element (Get_Index_Subtype_List (Prefix_Type),
                              Natural (Get_Value (Get_Parameter (Attr)) - 1));
   end Eval_Array_Attribute;

   function Eval_Integer_Image (Val : Iir_Int64; Orig : Iir) return Iir
   is
      use Str_Table;
      Img : String (1 .. 24); --  23 is enough, 24 is rounded.
      L : Natural;
      V : Iir_Int64;
      Id : String_Id;
   begin
      V := Val;
      L := Img'Last;
      loop
         Img (L) := Character'Val (Character'Pos ('0') + abs (V rem 10));
         V := V / 10;
         L := L - 1;
         exit when V = 0;
      end loop;
      if Val < 0 then
         Img (L) := '-';
         L := L - 1;
      end if;
      Id := Start;
      for I in L + 1 .. Img'Last loop
         Append (Img (I));
      end loop;
      Finish;
      return Build_String (Id, Int32 (Img'Last - L), Orig);
   end Eval_Integer_Image;

   function Eval_Floating_Image (Val : Iir_Fp64; Orig : Iir) return Iir
   is
      use Str_Table;
      Id : String_Id;

      --  Sign (1) + digit (1) + dot (1) + digits (15) + exp (1) + sign (1)
      --  + exp_digits (4) -> 24.
      Str : String (1 .. 25);
      P : Natural;
      V : Iir_Fp64;
      Vd : Iir_Fp64;
      Exp : Integer;
      D : Integer;
      B : Boolean;

      Res : Iir;
   begin
      --  Handle sign.
      if Val < 0.0 then
         Str (1) := '-';
         P := 1;
         V := -Val;
      else
         P := 0;
         V := Val;
      end if;

      --  Compute the mantissa.
      --  FIXME: should do a dichotomy.
      if V  = 0.0 then
         Exp := 0;
      elsif V < 1.0 then
         Exp := -1;
         while V * (10.0 ** (-Exp)) < 1.0 loop
            Exp := Exp - 1;
         end loop;
      else
         Exp := 0;
         while V / (10.0 ** Exp) >= 10.0 loop
            Exp := Exp + 1;
         end loop;
      end if;

      --  Normalize VAL: in [0; 10[
      if Exp >= 0 then
         V := V / (10.0 ** Exp);
      else
         V := V * 10.0 ** (-Exp);
      end if;

      for I in 0 .. 15 loop
         Vd := Iir_Fp64'Truncation (V);
         P := P + 1;
         Str (P) := Character'Val (48 + Integer (Vd));
         V := (V - Vd) * 10.0;

         if I = 0 then
            P := P + 1;
            Str (P) := '.';
         end if;
         exit when I > 0 and V < 10.0 ** (I + 1 - 15);
      end loop;

      if Exp /= 0 then
         --  LRM93 14.3
         --  if the exponent is present, the `e' is written as a lower case
         --  character.
         P := P + 1;
         Str (P) := 'e';

         if Exp < 0 then
            P := P + 1;
            Str (P) := '-';
            Exp := -Exp;
         end if;
         B := False;
         for I in 0 .. 4 loop
            D := (Exp / 10000) mod 10;
            if D /= 0 or B or I = 4 then
               P := P + 1;
               Str (P) := Character'Val (48 + D);
               B := True;
            end if;
            Exp := (Exp - D * 10000) * 10;
         end loop;
      end if;

      Id := Start;
      for I in 1 .. P loop
         Append (Str (I));
      end loop;
      Finish;
      Res := Build_String (Id, Int32 (P), Orig);
      --  FIXME: this is not correct since the type is *not* constrained.
      Set_Type (Res, Create_Unidim_Array_By_Length
                (Get_Type (Orig), Iir_Int64 (P), Orig));
      return Res;
   end Eval_Floating_Image;

   function Eval_Enumeration_Image (Enum, Expr : Iir) return Iir
   is
      Name : constant String := Image_Identifier (Enum);
      Image_Id : constant String_Id := Str_Table.Start;
   begin
      for i in Name'range loop
         Str_Table.Append(Name(i));
      end loop;
      Str_Table.Finish;
      return Build_String (Image_Id, Nat32(Name'Length), Expr);
   end Eval_Enumeration_Image;

   function Build_Enumeration_Value (Val : String; Enum, Expr : Iir) return Iir
   is
   Value : String(Val'range);
   List  : constant Iir_List := Get_Enumeration_Literal_List(Enum);
   begin
      for i in Val'range loop
         Value(i) := Ada.Characters.Handling.To_Lower (Val(i));
      end loop;
      for i in 0 .. Get_Nbr_Elements(List) - 1 loop
         if Value = Image_Identifier(Get_Nth_Element(List, i)) then
            return Build_Discrete(Iir_Int64(i), Expr);
         end if;
      end loop;
      Error_Msg_Sem ("value """ & Value & """ not in enumeration", Expr);
      return Null_Iir;
   end Build_Enumeration_Value;

   function Eval_Physical_Image (Phys, Expr: Iir) return Iir
   -- reduces to the base unit (e.g. femtoseconds)
   is
      Value : constant String := Iir_Int64'image(
                                    Get_Physical_Literal_Value(Phys));
      Unit : constant Iir := Get_Primary_Unit (Get_Base_Type (Get_Type(Phys)));
      UnitName : constant String := Image_Identifier (Unit);
      Image_Id : constant String_Id := Str_Table.Start;
      Length : Nat32 := Value'Length + UnitName'Length + 1;
   begin
      for i in Value'range loop
         -- Suppress the Ada +ve integer'image leading space
         if i > Value'first or else Value(i) /= ' ' then
            Str_Table.Append(Value(i));
         else
            Length := Length - 1;
         end if;
      end loop;
      Str_Table.Append(' ');
      for i in UnitName'range loop
         Str_Table.Append(UnitName(i));
      end loop;
      Str_Table.Finish;

      return Build_String (Image_Id, Length, Expr);
   end Eval_Physical_Image;

   function Build_Physical_Value (Val: String; Phys_Type, Expr: Iir) return Iir
   is
      function White (C : in Character) return Boolean is
         NBSP : constant Character := Character'Val (160);
         HT   : constant Character := Character'Val (9);
      begin
         return C = ' ' or C = NBSP or C = HT;
      end White;

      UnitName : String(Val'range);
      Sep : Natural;
      Found_Unit : Boolean := false;
      Found_Real : Boolean := false;
      Unit : Iir := Get_Primary_Unit (Phys_Type);
   begin
      -- Separate string into numeric value and make lowercase unit.
      for i in reverse Val'range loop
         UnitName(i) := Ada.Characters.Handling.To_Lower (Val(i));
         if White(Val(i)) and Found_Unit then
            Sep := i;
            exit;
         else
            Found_Unit := true;
         end if;
      end loop;
      -- Unit name  is UnitName(Sep+1..Unit'Last)
      for i in Val'first .. Sep loop
         if Val(i) = '.' then
            Found_Real := true;
         end if;
      end loop;
      -- Chain down the units looking for matching one
      Unit := Get_Primary_Unit (Phys_Type);
      while Unit /= Null_Iir loop
         exit when UnitName(Sep+1..UnitName'Last) = Image_Identifier(Unit);
         Unit := Get_Chain (Unit);
      end loop;
      if Unit = Null_Iir then
         Error_Msg_Sem ("Unit """ & UnitName(Sep+1..UnitName'Last)
                         & """ not in physical type", Expr);
         return Null_Iir;
      end if;
      if Found_Real then
         return Build_Physical(Iir_Int64(
                                    Iir_Fp64'value(Val(Val'first .. Sep)) *
                                    Iir_Fp64(Get_Value (Get_Physical_Unit_Value
                                    (Unit)))), Expr);
      else
         return Build_Physical(Iir_Int64'value(Val(Val'first .. Sep)) *
                             Get_Value (Get_Physical_Unit_Value(Unit)), Expr);
      end if;
   end Build_Physical_Value;


   function Eval_Incdec (Expr : Iir; N : Iir_Int64) return Iir
   is
      P : Iir_Int64;
   begin
      case Get_Kind (Expr) is
         when Iir_Kind_Integer_Literal =>
            return Build_Integer (Get_Value (Expr) + N, Expr);
         when Iir_Kind_Enumeration_Literal =>
            P := Iir_Int64 (Get_Enum_Pos (Expr)) + N;
            if P < 0 then
               Error_Msg_Sem ("static constant violates bounds", Expr);
               return Expr;
            else
               return Build_Enumeration (Iir_Index32 (P), Expr);
            end if;
         when Iir_Kind_Physical_Int_Literal
           | Iir_Kind_Unit_Declaration =>
            return Build_Physical (Get_Physical_Value (Expr) + N, Expr);
         when others =>
            Error_Kind ("eval_incdec", Expr);
      end case;
   end Eval_Incdec;

   function Convert_Range (Rng : Iir; Res_Type : Iir; Loc : Iir) return Iir
   is
      Res_Btype : Iir;

      function Create_Bound (Val : Iir) return Iir
      is
         R : Iir;
      begin
         R := Create_Iir (Iir_Kind_Integer_Literal);
         Location_Copy (R, Loc);
         Set_Value (R, Get_Value (Val));
         Set_Type (R, Res_Btype);
         Set_Expr_Staticness (R, Locally);
         return R;
      end Create_Bound;

      Res : Iir;
   begin
      Res_Btype := Get_Base_Type (Res_Type);
      Res := Create_Iir (Iir_Kind_Range_Expression);
      Location_Copy (Res, Loc);
      Set_Type (Res, Res_Btype);
      Set_Left_Limit (Res, Create_Bound (Get_Left_Limit (Rng)));
      Set_Right_Limit (Res, Create_Bound (Get_Right_Limit (Rng)));
      Set_Direction (Res, Get_Direction (Rng));
      Set_Expr_Staticness (Res, Locally);
      return Res;
   end Convert_Range;

   function Eval_Array_Type_Conversion (Conv : Iir; Val : Iir) return Iir
   is
      Conv_Type : Iir;
      Res : Iir;
      Val_Type : Iir;
      Conv_Index_Type : Iir;
      Val_Index_Type : Iir;
      Index_Type : Iir;
      Rng : Iir;
   begin
      Conv_Type := Get_Type (Conv);
      Conv_Index_Type := Get_Nth_Element
        (Get_Index_Subtype_List (Conv_Type), 0);
      Val_Type := Get_Type (Val);
      Val_Index_Type := Get_Nth_Element
        (Get_Index_Subtype_List (Val_Type), 0);

      --  The expression is either a simple aggregate or a (bit) string.
      Res := Build_Constant (Val, Conv);
      case Get_Kind (Conv_Type) is
         when Iir_Kind_Array_Subtype_Definition =>
            Set_Type (Res, Conv_Type);
            if Eval_Discrete_Type_Length (Conv_Index_Type)
              /= Eval_Discrete_Type_Length (Val_Index_Type)
            then
               Error_Msg_Sem ("non matching length in type convertion", Conv);
            end if;
            return Res;
         when Iir_Kind_Array_Type_Definition =>
            if Get_Base_Type (Conv_Index_Type) = Get_Base_Type (Val_Index_Type)
            then
               Index_Type := Val_Index_Type;
            else
               --  Convert the index range.
               --  It is an integer type.
               Rng := Convert_Range (Get_Range_Constraint (Val_Index_Type),
                                     Conv_Index_Type, Conv);
               Index_Type := Create_Iir (Iir_Kind_Integer_Subtype_Definition);
               Location_Copy (Index_Type, Conv);
               Set_Range_Constraint (Index_Type, Rng);
               Set_Base_Type (Index_Type, Get_Base_Type (Conv_Index_Type));
               Set_Type_Staticness (Index_Type, Locally);
            end if;
            Set_Type (Res,
                      Create_Unidim_Array_From_Index
                      (Get_Base_Type (Conv_Type), Index_Type, Conv));
            return Res;
         when others =>
            Error_Kind ("eval_array_type_conversion", Conv_Type);
      end case;
   end Eval_Array_Type_Conversion;

   function Eval_Type_Conversion (Expr : Iir) return Iir
   is
      Val : Iir;
      Val_Type : Iir;
      Conv_Type : Iir;
   begin
      Val := Eval_Expr (Get_Expression (Expr));
      Set_Expression (Expr, Val);
      Val_Type := Get_Base_Type (Get_Type (Val));
      Conv_Type := Get_Base_Type (Get_Type (Expr));
      if Conv_Type = Val_Type then
         return Build_Constant (Val, Expr);
      end if;
      case Get_Kind (Conv_Type) is
         when Iir_Kind_Integer_Type_Definition =>
            case Get_Kind (Val_Type) is
               when Iir_Kind_Integer_Type_Definition =>
                  return Build_Integer (Get_Value (Val), Expr);
               when Iir_Kind_Floating_Type_Definition =>
                  return Build_Integer (Iir_Int64 (Get_Fp_Value (Val)), Expr);
               when others =>
                  Error_Kind ("eval_type_conversion(1)", Val_Type);
            end case;
         when Iir_Kind_Floating_Type_Definition =>
            case Get_Kind (Val_Type) is
               when Iir_Kind_Integer_Type_Definition =>
                  return Build_Floating (Iir_Fp64 (Get_Value (Val)), Expr);
               when Iir_Kind_Floating_Type_Definition =>
                  return Build_Floating (Get_Fp_Value (Val), Expr);
               when others =>
                  Error_Kind ("eval_type_conversion(2)", Val_Type);
            end case;
         when Iir_Kind_Array_Type_Definition =>
            return Eval_Array_Type_Conversion (Expr, Val);
         when others =>
            Error_Kind ("eval_type_conversion(3)", Conv_Type);
      end case;
   end Eval_Type_Conversion;

   function Eval_Static_Expr (Expr: Iir) return Iir
   is
      Res : Iir;
      Val : Iir;
   begin
      case Get_Kind (Expr) is
         when Iir_Kind_Integer_Literal =>
            return Expr;
         when Iir_Kind_Enumeration_Literal =>
            return Expr;
         when Iir_Kind_Floating_Point_Literal =>
            return Expr;
         when Iir_Kind_String_Literal =>
            return Expr;
         when Iir_Kind_Bit_String_Literal =>
            return Expr;
         when Iir_Kind_Physical_Int_Literal =>
            if Get_Unit_Name (Expr)
              = Get_Primary_Unit (Get_Base_Type (Get_Type (Expr)))
            then
               return Expr;
            else
               return Build_Physical (Get_Physical_Value (Expr), Expr);
            end if;
         when Iir_Kind_Physical_Fp_Literal =>
            return Build_Physical
              (Iir_Int64 (Get_Fp_Value (Expr)
                          * Iir_Fp64 (Get_Value (Get_Physical_Unit_Value
                                                 (Get_Unit_Name (Expr))))),
               Expr);
         when Iir_Kind_Constant_Declaration =>
            Val := Get_Default_Value (Expr);
            Res := Build_Constant (Val, Expr);
            --  Type of the expression should be type of the constant
            --  declaration at least in case of array subtype.
            --  If the constant is declared as an unconstrained array, get type
            --  from the default value.
            --  FIXME: handle this during semantisation of the declaration.
            if Get_Kind (Get_Type (Res)) = Iir_Kind_Array_Type_Definition then
               Set_Type (Res, Get_Type (Val));
            end if;
            return Res;
         when Iir_Kind_Object_Alias_Declaration =>
            return Build_Constant (Eval_Static_Expr (Get_Name (Expr)), Expr);
         when Iir_Kind_Unit_Declaration =>
            return Expr;
         when Iir_Kind_Simple_Aggregate =>
            return Expr;

         when Iir_Kind_Qualified_Expression =>
            return Build_Constant (Eval_Expr (Get_Expression (Expr)), Expr);
         when Iir_Kind_Type_Conversion =>
            return Eval_Type_Conversion (Expr);
         when Iir_Kind_Range_Expression =>
            Set_Left_Limit (Expr, Eval_Expr (Get_Left_Limit (Expr)));
            Set_Right_Limit (Expr, Eval_Expr (Get_Right_Limit (Expr)));
            return Expr;

         when Iir_Kinds_Monadic_Operator =>
            declare
               Operand : Iir;
            begin
               Operand := Eval_Expr (Get_Operand (Expr));
               Set_Operand (Expr, Operand);
               return Eval_Monadic_Operator (Expr, Operand);
            end;
         when Iir_Kinds_Dyadic_Operator =>
            declare
               Left, Right : Iir;
            begin
               Left := Eval_Expr (Get_Left (Expr));
               Right := Eval_Expr (Get_Right (Expr));

               Set_Left (Expr, Left);
               Set_Right (Expr, Right);
               return Eval_Dyadic_Operator (Expr, Left, Right);
            end;

         when Iir_Kind_Attribute_Value =>
            --  FIXME.
            --  Currently, this avoids weird nodes, such as a string literal
            --  whose type is an unconstrained array type.
            Val := Get_Expression (Get_Attribute_Specification (Expr));
            Res := Build_Constant (Val, Expr);
            Set_Type (Res, Get_Type (Val));
            return Res;

         when Iir_Kind_Pos_Attribute =>
            declare
               Val : Iir;
            begin
               Val := Eval_Expr (Get_Parameter (Expr));
               Set_Parameter (Expr, Val);
               return Build_Integer (Eval_Pos (Val), Expr);
            end;
         when Iir_Kind_Val_Attribute =>
            declare
               Val_Expr : Iir;
               Val : Iir_Int64;
               Expr_Type : Iir;
            begin
               Val_Expr := Eval_Expr (Get_Parameter (Expr));
               Set_Parameter (Expr, Val_Expr);
               Val := Eval_Pos (Val_Expr);
               --  Note: the type of 'val is a base type.
               Expr_Type := Get_Type (Expr);
               --  FIXME: handle VHDL93 restrictions.
               if Get_Kind (Expr_Type) = Iir_Kind_Enumeration_Type_Definition
                 and then
                 not Eval_Int_In_Range (Val, Get_Range_Constraint (Expr_Type))
               then
                  Error_Msg_Sem
                    ("static argument out of the type range", Expr);
                  Val := 0;
               end if;
               if Get_Kind (Get_Base_Type (Get_Type (Expr)))
                 = Iir_Kind_Physical_Type_Definition
               then
                  return Build_Physical (Val, Expr);
               else
                  return Build_Discrete (Val, Expr);
               end if;
            end;
         when Iir_Kind_Image_Attribute =>
            declare
               Param : Iir;
               Param_Type : Iir;
            begin
               Param := Get_Parameter (Expr);
               Param := Eval_Static_Expr (Param);
               Set_Parameter (Expr, Param);
               Param_Type := Get_Base_Type (Get_Type (Param));
               case Get_Kind (Param_Type) is
                  when Iir_Kind_Integer_Type_Definition =>
                     return Eval_Integer_Image (Get_Value (Param), Expr);
                  when Iir_Kind_Floating_Type_Definition =>
                     return Eval_Floating_Image (Get_Fp_Value (Param), Expr);
                  when Iir_Kind_Enumeration_Type_Definition =>
                     return Eval_Enumeration_Image (Param, Expr);
                  when Iir_Kind_Physical_Type_Definition =>
                     return Eval_Physical_Image (Param, Expr);
                  when others =>
                     Error_Kind ("eval_static_expr('image)", Param);
               end case;
            end;
         when Iir_Kind_Value_Attribute =>
            declare
               Param : Iir;
               Param_Type : Iir;
            begin
               Param := Get_Parameter (Expr);
               Param := Eval_Static_Expr (Param);
               Set_Parameter (Expr, Param);
               if Get_Kind (Param) /= Iir_Kind_String_Literal then
                  Error_Msg_Sem ("'value argument not a string", Expr);
                  return Null_Iir; -- or Expr?
               else
                  -- what type are we converting the string to?
                  Param_Type := Get_Base_Type (Get_Type (Expr));
                  declare
                     Value : constant String := Image_String_Lit(Param);
                  begin
                     case Get_Kind (Param_Type) is
                     when Iir_Kind_Integer_Type_Definition =>
                        return Build_Discrete(Iir_Int64'value(Value), Expr);
                     when Iir_Kind_Enumeration_Type_Definition =>
                        return Build_Enumeration_Value (Value, Param_Type,
                                                        Expr);
                     when Iir_Kind_Floating_Type_Definition =>
                        return Build_Floating (Iir_Fp64'value (Value), Expr);
                     when Iir_Kind_Physical_Type_Definition =>
                        return Build_Physical_Value (Value, Param_Type, Expr);
                     when others =>
                        Error_Kind ("eval_static_expr('value)", Param);
                     end case;
                  end;
               end if;
            end;

         when Iir_Kind_Left_Type_Attribute =>
            return Build_Constant
              (Get_Left_Limit (Eval_Range (Get_Prefix (Expr))), Expr);
         when Iir_Kind_Right_Type_Attribute =>
            return Build_Constant
              (Get_Right_Limit (Eval_Range (Get_Prefix (Expr))), Expr);
         when Iir_Kind_High_Type_Attribute =>
            return Build_Constant
              (Get_High_Limit (Eval_Range (Get_Prefix (Expr))), Expr);
         when Iir_Kind_Low_Type_Attribute =>
            return Build_Constant
              (Get_Low_Limit (Eval_Range (Get_Prefix (Expr))), Expr);
         when Iir_Kind_Ascending_Type_Attribute =>
            return Build_Boolean
              (Get_Direction (Eval_Range (Get_Prefix (Expr))) = Iir_To, Expr);

         when Iir_Kind_Range_Array_Attribute =>
            declare
               Index : Iir;
            begin
               Index := Eval_Array_Attribute (Expr);
               return Get_Range_Constraint (Index);
            end;
         when Iir_Kind_Reverse_Range_Array_Attribute =>
            declare
               Res : Iir;
               Rng : Iir;
            begin
               Rng := Get_Range_Constraint (Eval_Array_Attribute (Expr));
               Res := Create_Iir (Iir_Kind_Range_Expression);
               Location_Copy (Res, Rng);
               Set_Type (Res, Get_Type (Rng));
               case Get_Direction (Rng) is
                  when Iir_To =>
                     Set_Direction (Res, Iir_Downto);
                  when Iir_Downto =>
                     Set_Direction (Res, Iir_To);
               end case;
               Set_Left_Limit (Res, Get_Right_Limit (Rng));
               Set_Right_Limit (Res, Get_Left_Limit (Rng));
               -- FIXME: todo.
               --Set_Literal_Origin (Res, Rng);
               Set_Expr_Staticness (Res, Get_Expr_Staticness (Rng));
               return Res;
            end;
         when Iir_Kind_Length_Array_Attribute =>
            declare
               Index : Iir;
            begin
               Index := Eval_Array_Attribute (Expr);
               return Build_Discrete (Eval_Discrete_Type_Length (Index), Expr);
            end;
         when Iir_Kind_Left_Array_Attribute =>
            declare
               Index : Iir;
            begin
               Index := Eval_Array_Attribute (Expr);
               return Build_Constant
                 (Get_Left_Limit (Get_Range_Constraint (Index)), Expr);
            end;
         when Iir_Kind_Right_Array_Attribute =>
            declare
               Index : Iir;
            begin
               Index := Eval_Array_Attribute (Expr);
               return Build_Constant
                 (Get_Right_Limit (Get_Range_Constraint (Index)), Expr);
            end;
         when Iir_Kind_Low_Array_Attribute =>
            declare
               Index : Iir;
            begin
               Index := Eval_Array_Attribute (Expr);
               return Build_Constant
                 (Get_Low_Limit (Get_Range_Constraint (Index)), Expr);
            end;
         when Iir_Kind_High_Array_Attribute =>
            declare
               Index : Iir;
            begin
               Index := Eval_Array_Attribute (Expr);
               return Build_Constant
                 (Get_High_Limit (Get_Range_Constraint (Index)), Expr);
            end;
         when Iir_Kind_Ascending_Array_Attribute =>
            declare
               Index : Iir;
            begin
               Index := Eval_Array_Attribute (Expr);
               return Build_Boolean
                 (Get_Direction (Get_Range_Constraint (Index)) = Iir_To, Expr);
            end;

         when Iir_Kind_Pred_Attribute =>
            Res := Eval_Incdec (Eval_Static_Expr (Get_Parameter (Expr)), -1);
            Eval_Check_Bound (Res, Get_Type (Get_Prefix (Expr)));
            return Res;
         when Iir_Kind_Succ_Attribute =>
            Res := Eval_Incdec (Eval_Static_Expr (Get_Parameter (Expr)), +1);
            Eval_Check_Bound (Res, Get_Type (Get_Prefix (Expr)));
            return Res;
         when Iir_Kind_Leftof_Attribute
           | Iir_Kind_Rightof_Attribute =>
            declare
               Rng : Iir;
               N : Iir_Int64;
               Prefix_Type : Iir;
               Res : Iir;
            begin
               Prefix_Type := Get_Type (Get_Prefix (Expr));
               Rng := Eval_Range (Prefix_Type);
               case Get_Direction (Rng) is
                  when Iir_To =>
                     N := 1;
                  when Iir_Downto =>
                     N := -1;
               end case;
               case Get_Kind (Expr) is
                  when Iir_Kind_Leftof_Attribute =>
                     N := -N;
                  when Iir_Kind_Rightof_Attribute =>
                     null;
                  when others =>
                     raise Internal_Error;
               end case;
               Res := Eval_Incdec (Eval_Static_Expr (Get_Parameter (Expr)), N);
               Eval_Check_Bound (Res, Prefix_Type);
               return Res;
            end;

         when Iir_Kind_Simple_Name_Attribute =>
            declare
               use Str_Table;
               Id : String_Id;
            begin
               Id := Start;
               Image (Get_Simple_Name_Identifier (Expr));
               for I in 1 .. Name_Length loop
                  Append (Name_Buffer (I));
               end loop;
               Finish;
               return Build_String (Id, Nat32 (Name_Length), Expr);
            end;

         when Iir_Kind_Null_Literal =>
            return Expr;

         when Iir_Kind_Function_Call =>
            declare
               Left, Right : Iir;
            begin
               --  Note: there can't be association by name.
               Left := Get_Parameter_Association_Chain (Expr);
               Right := Get_Chain (Left);
               if Right = Null_Iir then
                  return Eval_Monadic_Operator (Expr, Get_Actual (Left));
               else
                  return Eval_Dyadic_Operator
                    (Expr, Get_Actual (Left), Get_Actual (Right));
               end if;
            end;

         when Iir_Kind_Simple_Name
           | Iir_Kind_Selected_Name =>
            declare
               Res : Iir;
               Orig : Iir;
            begin
               Orig := Get_Named_Entity (Expr);
               Res := Eval_Static_Expr (Orig);
               if Res /= Orig then
                  Location_Copy (Res, Expr);
               end if;
               Free_Name (Expr);
               return Res;
            end;
         when Iir_Kind_Error =>
            return Expr;
         when others =>
            Error_Kind ("eval_static_expr", Expr);
      end case;
   end Eval_Static_Expr;

   function Eval_Expr (Expr: Iir) return Iir is
   begin
      if Get_Expr_Staticness (Expr) /= Locally then
         Error_Msg_Sem ("expression must be locally static", Expr);
         return Expr;
      else
         return Eval_Static_Expr (Expr);
      end if;
   end Eval_Expr;

   function Eval_Expr_If_Static (Expr : Iir) return Iir is
   begin
      if Expr /= Null_Iir and then Get_Expr_Staticness (Expr) = Locally then
         return Eval_Static_Expr (Expr);
      else
         return Expr;
      end if;
   end Eval_Expr_If_Static;

   function Eval_Expr_Check_If_Static (Expr : Iir; Atype : Iir) return Iir
   is
      Res : Iir;
   begin
      if Expr /= Null_Iir and then Get_Expr_Staticness (Expr) = Locally then
         Res := Eval_Expr (Expr);
         if Res /= Null_Iir
           and then Get_Type_Staticness (Atype) = Locally
           and then Get_Kind (Atype) in Iir_Kinds_Range_Type_Definition
         then
            Eval_Check_Bound (Res, Atype);
         end if;
         return Res;
      else
         return Expr;
      end if;
   end Eval_Expr_Check_If_Static;

   function Eval_Int_In_Range (Val : Iir_Int64; Bound : Iir) return Boolean is
   begin
      case Get_Kind (Bound) is
         when Iir_Kind_Range_Expression =>
            case Get_Direction (Bound) is
               when Iir_To =>
                  if Val < Eval_Pos (Get_Left_Limit (Bound))
                    or else Val > Eval_Pos (Get_Right_Limit (Bound))
                  then
                     return False;
                  end if;
               when Iir_Downto =>
                  if Val > Eval_Pos (Get_Left_Limit (Bound))
                    or else Val < Eval_Pos (Get_Right_Limit (Bound))
                  then
                     return False;
                  end if;
            end case;
         when others =>
            Error_Kind ("eval_int_in_range", Bound);
      end case;
      return True;
   end Eval_Int_In_Range;

   function Eval_Phys_In_Range (Val : Iir_Int64; Bound : Iir) return Boolean
   is
      Left, Right : Iir_Int64;
   begin
      case Get_Kind (Bound) is
         when Iir_Kind_Range_Expression =>
            case Get_Kind (Get_Type (Get_Left_Limit (Bound))) is
               when Iir_Kind_Integer_Type_Definition
                 | Iir_Kind_Integer_Subtype_Definition =>
                  Left := Get_Value (Get_Left_Limit (Bound));
                  Right := Get_Value (Get_Right_Limit (Bound));
               when Iir_Kind_Physical_Type_Definition
                 | Iir_Kind_Physical_Subtype_Definition =>
                  Left := Get_Physical_Value (Get_Left_Limit (Bound));
                  Right := Get_Physical_Value (Get_Right_Limit (Bound));
               when others =>
                  Error_Kind ("eval_phys_in_range(1)", Get_Type (Bound));
            end case;
            case Get_Direction (Bound) is
               when Iir_To =>
                  if Val < Left or else Val > Right then
                     return False;
                  end if;
               when Iir_Downto =>
                  if Val > Left or else Val < Right then
                     return False;
                  end if;
            end case;
         when others =>
            Error_Kind ("eval_phys_in_range", Bound);
      end case;
      return True;
   end Eval_Phys_In_Range;

   function Eval_Fp_In_Range (Val : Iir_Fp64; Bound : Iir) return Boolean is
   begin
      case Get_Kind (Bound) is
         when Iir_Kind_Range_Expression =>
            case Get_Direction (Bound) is
               when Iir_To =>
                  if Val < Get_Fp_Value (Get_Left_Limit (Bound))
                    or else Val > Get_Fp_Value (Get_Right_Limit (Bound))
                  then
                     return False;
                  end if;
               when Iir_Downto =>
                  if Val > Get_Fp_Value (Get_Left_Limit (Bound))
                    or else Val < Get_Fp_Value (Get_Right_Limit (Bound))
                  then
                     return False;
                  end if;
            end case;
         when others =>
            Error_Kind ("eval_fp_in_range", Bound);
      end case;
      return True;
   end Eval_Fp_In_Range;

   --  Return TRUE if literal EXPR is in SUB_TYPE bounds.
   function Eval_Is_In_Bound (Expr : Iir; Sub_Type : Iir)
     return Boolean
   is
      Type_Range : Iir;
   begin
      if Get_Kind (Expr) = Iir_Kind_Error then
         return True;
      end if;

      case Get_Kind (Sub_Type) is
         when Iir_Kind_Integer_Subtype_Definition =>
            Type_Range := Get_Range_Constraint (Sub_Type);
            return Eval_Int_In_Range (Get_Value (Expr), Type_Range);
         when Iir_Kind_Floating_Subtype_Definition =>
            Type_Range := Get_Range_Constraint (Sub_Type);
            return Eval_Fp_In_Range (Get_Fp_Value (Expr), Type_Range);
         when Iir_Kind_Enumeration_Subtype_Definition
           | Iir_Kind_Enumeration_Type_Definition =>
            --  A check is required for an enumeration type definition for
            --  'val attribute.
            Type_Range := Get_Range_Constraint (Sub_Type);
            return Eval_Int_In_Range
              (Iir_Int64 (Get_Enum_Pos (Expr)), Type_Range);
         when Iir_Kind_Physical_Subtype_Definition =>
            Type_Range := Get_Range_Constraint (Sub_Type);
            return Eval_Phys_In_Range (Get_Physical_Value (Expr), Type_Range);

         when Iir_Kind_Base_Attribute =>
            return Eval_Is_In_Bound (Expr, Get_Type (Sub_Type));

         when Iir_Kind_Array_Subtype_Definition
           | Iir_Kind_Array_Type_Definition
           | Iir_Kind_Record_Type_Definition =>
            --  FIXME: do it.
            return True;

         --when Iir_Kind_Integer_Type_Definition =>
            --  This case should not happen but it may be called to check a
            --  simple choice value belongs to the *type* of the case
            --  expression.
            --  Of course, this is always true.
         --   return True;

         when others =>
            Error_Kind ("eval_is_in_bound", Sub_Type);
            return False;
      end case;
   end Eval_Is_In_Bound;

   procedure Eval_Check_Bound (Expr : Iir; Sub_Type : Iir)
   is
   begin
      if not Eval_Is_In_Bound (Expr, Sub_Type) then
         Error_Msg_Sem ("static constant violates bounds", Expr);
      end if;
   end Eval_Check_Bound;

   function Eval_Is_Range_In_Bound
     (A_Range : Iir; Sub_Type : Iir; Any_Dir : Boolean)
     return Boolean
   is
      Type_Range : Iir;
   begin
      Type_Range := Get_Range_Constraint (Sub_Type);
      if not Any_Dir
        and then Get_Direction (Type_Range) /= Get_Direction (A_Range)
      then
         return True;
      end if;

      case Get_Kind (Sub_Type) is
         when Iir_Kind_Integer_Subtype_Definition
           | Iir_Kind_Physical_Subtype_Definition
           | Iir_Kind_Enumeration_Subtype_Definition
           | Iir_Kind_Enumeration_Type_Definition =>
            declare
               L, R : Iir_Int64;
            begin
               --  Check for null range.
               L := Eval_Pos (Get_Left_Limit (A_Range));
               R := Eval_Pos (Get_Right_Limit (A_Range));
               case Get_Direction (A_Range) is
                  when Iir_To =>
                     if L > R then
                        return True;
                     end if;
                  when Iir_Downto =>
                     if L < R then
                        return True;
                     end if;
               end case;
               return Eval_Int_In_Range (L, Type_Range)
                 and then Eval_Int_In_Range (R, Type_Range);
            end;
         when Iir_Kind_Floating_Subtype_Definition =>
            declare
               L, R : Iir_Fp64;
            begin
               --  Check for null range.
               L := Get_Fp_Value (Get_Left_Limit (A_Range));
               R := Get_Fp_Value (Get_Right_Limit (A_Range));
               case Get_Direction (A_Range) is
                  when Iir_To =>
                     if L > R then
                        return True;
                     end if;
                  when Iir_Downto =>
                     if L < R then
                        return True;
                     end if;
               end case;
               return Eval_Fp_In_Range (L, Type_Range)
                 and then Eval_Fp_In_Range (R, Type_Range);
            end;
         when others =>
            Error_Kind ("eval_is_range_in_bound", Sub_Type);
      end case;

      --  Should check L <= R or L >= R according to direction.
      --return Eval_Is_In_Bound (Get_Left_Limit (A_Range), Sub_Type)
      --  and then Eval_Is_In_Bound (Get_Right_Limit (A_Range), Sub_Type);
   exception
      when Node_Error =>
         --  Avoid error storms.
         return True;
   end Eval_Is_Range_In_Bound;

   procedure Eval_Check_Range
     (A_Range : Iir; Sub_Type : Iir; Any_Dir : Boolean)
   is
   begin
      if not Eval_Is_Range_In_Bound (A_Range, Sub_Type, Any_Dir) then
         Error_Msg_Sem ("static range violates bounds", A_Range);
      end if;
   end Eval_Check_Range;

   function Eval_Expr_Check (Expr : Iir; Sub_Type : Iir) return Iir
   is
      Res : Iir;
   begin
      Res := Eval_Expr (Expr);
      Eval_Check_Bound (Res, Sub_Type);
      return Res;
   end Eval_Expr_Check;

   function Eval_Discrete_Range_Length (Constraint : Iir) return Iir_Int64
   is
      Res : Iir_Int64;
      Left, Right : Iir_Int64;
   begin
      Left := Eval_Pos (Get_Left_Limit (Constraint));
      Right := Eval_Pos (Get_Right_Limit (Constraint));
      case Get_Direction (Constraint) is
         when Iir_To =>
            if Right < Left then
               --  Null range.
               return 0;
            else
               Res := Right - Left + 1;
            end if;
         when Iir_Downto =>
            if Left < Right then
               --  Null range
               return 0;
            else
               Res := Left - Right + 1;
            end if;
      end case;
      return Res;
   end Eval_Discrete_Range_Length;

   function Eval_Discrete_Type_Length (Sub_Type : Iir) return Iir_Int64
   is
   begin
      case Get_Kind (Sub_Type) is
         when Iir_Kind_Enumeration_Subtype_Definition
           | Iir_Kind_Enumeration_Type_Definition
           | Iir_Kind_Integer_Subtype_Definition =>
            return Eval_Discrete_Range_Length
              (Get_Range_Constraint (Sub_Type));
         when others =>
            Error_Kind ("eval_discrete_type_length", Sub_Type);
      end case;
   end Eval_Discrete_Type_Length;

   function Eval_Pos (Expr : Iir) return Iir_Int64 is
   begin
      case Get_Kind (Expr) is
         when Iir_Kind_Integer_Literal =>
            return Get_Value (Expr);
         when Iir_Kind_Enumeration_Literal =>
            return Iir_Int64 (Get_Enum_Pos (Expr));
         when Iir_Kind_Physical_Int_Literal =>
            return Get_Physical_Value (Expr);
         when Iir_Kind_Unit_Declaration =>
            return Get_Value (Get_Physical_Unit_Value (Expr));
         when Iir_Kind_Error =>
            raise Node_Error;
         when others =>
            Error_Kind ("eval_pos", Expr);
      end case;
   end Eval_Pos;

   function Eval_Range (Rng : Iir) return Iir
   is
      Expr : Iir;
   begin
      Expr := Rng;
      loop
         case Get_Kind (Expr) is
            when Iir_Kind_Range_Expression =>
               return Expr;
            when Iir_Kind_Integer_Subtype_Definition
              | Iir_Kind_Floating_Subtype_Definition
              | Iir_Kind_Enumeration_Type_Definition
              | Iir_Kind_Enumeration_Subtype_Definition
              | Iir_Kind_Physical_Subtype_Definition =>
               Expr := Get_Range_Constraint (Expr);
            when Iir_Kind_Range_Array_Attribute =>
               declare
                  Prefix : Iir;
               begin
                  Prefix := Get_Prefix (Expr);
                  if Get_Kind (Prefix) /= Iir_Kind_Array_Subtype_Definition
                  then
                     Prefix := Get_Type (Prefix);
                  end if;
                  if Get_Kind (Prefix) /= Iir_Kind_Array_Subtype_Definition
                  then
                     --  Unconstrained object.
                     return Null_Iir;
                  end if;
                  Expr := Get_Nth_Element
                    (Get_Index_Subtype_List (Prefix),
                     Natural (Eval_Pos (Get_Parameter (Expr))) - 1);
               end;
            when Iir_Kind_Subtype_Declaration
              | Iir_Kind_Type_Declaration
              | Iir_Kind_Base_Attribute =>
               return Eval_Range (Get_Type (Expr));
            when others =>
               Error_Kind ("eval_range", Expr);
         end case;
      end loop;
   end Eval_Range;

   --  Return the range constraint of a discrete range.
   function Eval_Discrete_Range_Expression (Constraint : Iir) return Iir
   is
      Res : Iir;
   begin
      Res := Eval_Range (Constraint);
      if Res = Null_Iir then
         Error_Kind ("eval_range_expression", Constraint);
      else
         return Res;
      end if;
   end Eval_Discrete_Range_Expression;

   function Eval_Discrete_Range_Left (Constraint : Iir) return Iir
   is
      Range_Expr : Iir;
   begin
      Range_Expr := Eval_Discrete_Range_Expression (Constraint);
      return Get_Left_Limit (Range_Expr);
   end Eval_Discrete_Range_Left;

   procedure Eval_Operator_Symbol_Name (Id : Name_Id)
   is
   begin
      Image (Id);
      Name_Buffer (2 .. Name_Length + 1) := Name_Buffer (1 .. Name_Length);
      Name_Buffer (1) := '"'; --"
      Name_Length := Name_Length + 2;
      Name_Buffer (Name_Length) := '"'; --"
   end Eval_Operator_Symbol_Name;

   procedure Eval_Simple_Name (Id : Name_Id)
   is
   begin
      --  LRM 14.1
      --  E'SIMPLE_NAME
      --    Result: [...] but with apostrophes (in the case of a character
      --            literal)
      if Is_Character (Id) then
         Name_Buffer (1) := ''';
         Name_Buffer (2) := Get_Character (Id);
         Name_Buffer (3) := ''';
         Name_Length := 3;
         return;
      end if;
      case Id is
         when Std_Names.Name_Word_Operators
           | Std_Names.Name_First_Operator .. Std_Names.Name_Last_Operator =>
            Eval_Operator_Symbol_Name (Id);
            return;
         when Std_Names.Name_Xnor
           | Std_Names.Name_Shift_Operators =>
            if Flags.Vhdl_Std > Vhdl_87 then
               Eval_Operator_Symbol_Name (Id);
               return;
            end if;
         when others =>
            null;
      end case;
      Image (Id);
--       if Name_Buffer (1) = '\' then
--          declare
--             I : Natural;
--          begin
--             I := 2;
--             while I <= Name_Length loop
--                if Name_Buffer (I) = '\' then
--                   Name_Length := Name_Length + 1;
--                   Name_Buffer (I + 1 .. Name_Length) :=
--                     Name_Buffer (I .. Name_Length - 1);
--                   I := I + 1;
--                end if;
--                I := I + 1;
--             end loop;
--             Name_Length := Name_Length + 1;
--             Name_Buffer (Name_Length) := '\';
--          end;
--       end if;
   end Eval_Simple_Name;


   function Compare_String_Literals (L, R : Iir) return Compare_Type
   is
      type Str_Info is record
         El : Iir;
         Ptr : String_Fat_Acc;
         Len : Nat32;
         Lit_0 : Iir;
         Lit_1 : Iir;
         List : Iir_List;
      end record;

      Literal_List : Iir_List;

      --  Fill Res from EL.  This is used to speed up Lt and Eq operations.
      procedure Get_Info (Expr : Iir; Res : out Str_Info) is
      begin
         case Get_Kind (Expr) is
            when Iir_Kind_Simple_Aggregate =>
               Res := Str_Info'(El => Expr,
                                Ptr => null,
                                Len => 0,
                                Lit_0 | Lit_1 => Null_Iir,
                                List => Get_Simple_Aggregate_List (Expr));
               Res.Len := Nat32 (Get_Nbr_Elements (Res.List));
            when Iir_Kind_Bit_String_Literal =>
               Res := Str_Info'(El => Expr,
                                Ptr => Get_String_Fat_Acc (Expr),
                                Len => Get_String_Length (Expr),
                                Lit_0 => Get_Bit_String_0 (Expr),
                                Lit_1 => Get_Bit_String_1 (Expr),
                                List => Null_Iir_List);
            when Iir_Kind_String_Literal =>
               Res := Str_Info'(El => Expr,
                                Ptr => Get_String_Fat_Acc (Expr),
                                Len => Get_String_Length (Expr),
                                Lit_0 | Lit_1 => Null_Iir,
                                List => Null_Iir_List);
            when others =>
               Error_Kind ("sem_string_choice_range.get_info", Expr);
         end case;
      end Get_Info;

      --  Return the position of element IDX of STR.
      function Get_Pos (Str : Str_Info; Idx : Nat32) return Iir_Int32
      is
         S : Iir;
         C : Character;
      begin
         case Get_Kind (Str.El) is
            when Iir_Kind_Simple_Aggregate =>
               S := Get_Nth_Element (Str.List, Natural (Idx));
            when Iir_Kind_String_Literal =>
               C := Str.Ptr (Idx + 1);
               --  FIXME: build a table from character to position.
               --  This linear search is O(n)!
               S := Find_Name_In_List (Literal_List,
                                       Name_Table.Get_Identifier (C));
               if S = Null_Iir then
                  return -1;
               end if;
            when Iir_Kind_Bit_String_Literal =>
               C := Str.Ptr (Idx + 1);
               case C is
                  when '0' =>
                     S := Str.Lit_0;
                  when '1' =>
                     S := Str.Lit_1;
                  when others =>
                     raise Internal_Error;
               end case;
            when others =>
               Error_Kind ("sem_string_choice_range.get_pos", Str.El);
         end case;
         return Get_Enum_Pos (S);
      end Get_Pos;

      L_Info, R_Info : Str_Info;
      L_Pos, R_Pos : Iir_Int32;
   begin
      Get_Info (L, L_Info);
      Get_Info (R, R_Info);

      if L_Info.Len /= R_Info.Len then
         raise Internal_Error;
      end if;

      Literal_List := Get_Enumeration_Literal_List
        (Get_Base_Type (Get_Element_Subtype (Get_Type (L))));

      for I in 0 .. L_Info.Len - 1 loop
         L_Pos := Get_Pos (L_Info, I);
         R_Pos := Get_Pos (R_Info, I);
         if L_Pos /= R_Pos then
            if L_Pos < R_Pos then
               return Compare_Lt;
            else
               return Compare_Gt;
            end if;
         end if;
      end loop;
      return Compare_Eq;
   end Compare_String_Literals;

end Evaluation;