[multiple changes]

2016-10-12  Bob Duff  <duff@adacore.com>

	* xref_lib.adb: Use renamings-of-slices to ensure
	that all references to Tables are properly bounds checked (when
	checks are turned on).
	* g-dyntab.ads, g-dyntab.adb: Default-initialize the array
	components, so we don't get uninitialized pointers in case
	of Tables containing access types.  Misc cleanup of the code
	and comments.

2016-10-12  Ed Schonberg  <schonberg@adacore.com>

	* sem_attr.adb (Analyze_Attribute, case 'Type_Key): Implement
	functionality of attribute, to provide a reasonably unique key
	for a given type and detect any changes in the semantics of the
	type or any of its subcomponents from version to version.

2016-10-12  Bob Duff  <duff@adacore.com>

	* sem_case.adb (Check_Choice_Set): Separate
	checking for duplicates out into a separate pass from checking
	full coverage, because the check for duplicates does not depend
	on predicates. Therefore, we shouldn't do it separately for the
	predicate vs. no-predicate case; we should share code. The code
	for the predicate case was wrong.

From-SVN: r241039
This commit is contained in:
Arnaud Charlet 2016-10-12 14:55:47 +02:00
parent 6e8323274a
commit 84a62ce88b
6 changed files with 563 additions and 362 deletions

View File

@ -1,3 +1,29 @@
2016-10-12 Bob Duff <duff@adacore.com>
* xref_lib.adb: Use renamings-of-slices to ensure
that all references to Tables are properly bounds checked (when
checks are turned on).
* g-dyntab.ads, g-dyntab.adb: Default-initialize the array
components, so we don't get uninitialized pointers in case
of Tables containing access types. Misc cleanup of the code
and comments.
2016-10-12 Ed Schonberg <schonberg@adacore.com>
* sem_attr.adb (Analyze_Attribute, case 'Type_Key): Implement
functionality of attribute, to provide a reasonably unique key
for a given type and detect any changes in the semantics of the
type or any of its subcomponents from version to version.
2016-10-12 Bob Duff <duff@adacore.com>
* sem_case.adb (Check_Choice_Set): Separate
checking for duplicates out into a separate pass from checking
full coverage, because the check for duplicates does not depend
on predicates. Therefore, we shouldn't do it separately for the
predicate vs. no-predicate case; we should share code. The code
for the predicate case was wrong.
2016-10-12 Jerome Lambourg <lambourg@adacore.com>
* init.c: Make sure to call finit on x86_64-vx7 to reinitialize

View File

@ -6,7 +6,7 @@
-- --
-- B o d y --
-- --
-- Copyright (C) 2000-2014, AdaCore --
-- Copyright (C) 2000-2016, AdaCore --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
@ -32,33 +32,23 @@
pragma Compiler_Unit_Warning;
with GNAT.Heap_Sort_G;
with System; use System;
with System.Memory; use System.Memory;
with Ada.Unchecked_Conversion;
with Ada.Unchecked_Deallocation;
package body GNAT.Dynamic_Tables is
Min : constant Integer := Integer (Table_Low_Bound);
-- Subscript of the minimum entry in the currently allocated table
Empty : constant Table_Ptr :=
Empty_Table_Array_Ptr_To_Table_Ptr (Empty_Table_Array'Access);
-----------------------
-- Local Subprograms --
-----------------------
procedure Reallocate (T : in out Instance);
-- Reallocate the existing table according to the current value stored
-- in Max. Works correctly to do an initial allocation if the table
-- is currently null.
pragma Warnings (Off);
-- These unchecked conversions are in fact safe, since they never
-- generate improperly aliased pointer values.
function To_Address is new Ada.Unchecked_Conversion (Table_Ptr, Address);
function To_Pointer is new Ada.Unchecked_Conversion (Address, Table_Ptr);
pragma Warnings (On);
procedure Grow (T : in out Instance; New_Last : Table_Count_Type);
-- This is called when we are about to set the value of Last to a value
-- that is larger than Last_Allocated. This reallocates the table to the
-- larger size, as indicated by New_Last. At the time this is called,
-- T.P.Last is still the old value.
--------------
-- Allocate --
@ -66,11 +56,9 @@ package body GNAT.Dynamic_Tables is
procedure Allocate (T : in out Instance; Num : Integer := 1) is
begin
T.P.Last_Val := T.P.Last_Val + Num;
-- Note that Num can be negative
if T.P.Last_Val > T.P.Max then
Reallocate (T);
end if;
Set_Last (T, T.P.Last + Table_Index_Type'Base (Num));
end Allocate;
------------
@ -79,7 +67,7 @@ package body GNAT.Dynamic_Tables is
procedure Append (T : in out Instance; New_Val : Table_Component_Type) is
begin
Set_Item (T, Table_Index_Type (T.P.Last_Val + 1), New_Val);
Set_Item (T, T.P.Last + 1, New_Val);
end Append;
----------------
@ -99,9 +87,18 @@ package body GNAT.Dynamic_Tables is
procedure Decrement_Last (T : in out Instance) is
begin
T.P.Last_Val := T.P.Last_Val - 1;
Allocate (T, -1);
end Decrement_Last;
-----------
-- First --
-----------
function First return Table_Index_Type is
begin
return Table_Low_Bound;
end First;
--------------
-- For_Each --
--------------
@ -109,7 +106,7 @@ package body GNAT.Dynamic_Tables is
procedure For_Each (Table : Instance) is
Quit : Boolean := False;
begin
for Index in Table_Low_Bound .. Table_Index_Type (Table.P.Last_Val) loop
for Index in Table_Low_Bound .. Table.P.Last loop
Action (Index, Table.Table (Index), Quit);
exit when Quit;
end loop;
@ -120,23 +117,119 @@ package body GNAT.Dynamic_Tables is
----------
procedure Free (T : in out Instance) is
subtype Alloc_Type is Table_Type (First .. T.P.Last_Allocated);
type Alloc_Ptr is access all Alloc_Type;
procedure Free is new Ada.Unchecked_Deallocation (Alloc_Type, Alloc_Ptr);
function To_Alloc_Ptr is
new Ada.Unchecked_Conversion (Table_Ptr, Alloc_Ptr);
Temp : Alloc_Ptr := To_Alloc_Ptr (T.Table);
begin
Free (To_Address (T.Table));
T.Table := null;
T.P.Length := 0;
if T.Table = Empty then
pragma Assert (T.P.Last_Allocated = First - 1);
pragma Assert (T.P.Last = First - 1);
null;
else
Free (Temp);
T.Table := Empty;
T.P.Last_Allocated := First - 1;
T.P.Last := First - 1;
end if;
end Free;
----------
-- Grow --
----------
procedure Grow (T : in out Instance; New_Last : Table_Count_Type) is
-- Note: Type Alloc_Ptr below needs to be declared locally so we know
-- the bounds. That means that the collection is local, so is finalized
-- when leaving Grow. That's why this package doesn't support controlled
-- types; the table elements would be finalized prematurely. An Ada
-- implementation would also be within its rights to reclaim the
-- storage. Fortunately, GNAT doesn't do that.
pragma Assert (not T.Locked);
pragma Assert (New_Last > T.P.Last_Allocated);
subtype Table_Length_Type is Table_Index_Type'Base
range 0 .. Table_Index_Type'Base'Last;
Old_Last_Allocated : constant Table_Count_Type := T.P.Last_Allocated;
Old_Allocated_Length : constant Table_Length_Type :=
Old_Last_Allocated - First + 1;
New_Length : constant Table_Length_Type := New_Last - First + 1;
New_Allocated_Length : Table_Length_Type;
begin
if T.Table = Empty then
New_Allocated_Length := Table_Length_Type (Table_Initial);
else
New_Allocated_Length :=
Table_Length_Type
(Long_Long_Integer (Old_Allocated_Length) *
(100 + Long_Long_Integer (Table_Increment)) / 100);
end if;
-- Make sure it really did grow
if New_Allocated_Length <= Old_Allocated_Length then
New_Allocated_Length := Old_Allocated_Length + 10;
end if;
if New_Allocated_Length <= New_Length then
New_Allocated_Length := New_Length + 10;
end if;
pragma Assert (New_Allocated_Length > Old_Allocated_Length);
pragma Assert (New_Allocated_Length > New_Length);
T.P.Last_Allocated := First + New_Allocated_Length - 1;
declare
subtype Old_Alloc_Type is Table_Type (First .. Old_Last_Allocated);
type Old_Alloc_Ptr is access all Old_Alloc_Type;
procedure Free is
new Ada.Unchecked_Deallocation (Old_Alloc_Type, Old_Alloc_Ptr);
function To_Old_Alloc_Ptr is
new Ada.Unchecked_Conversion (Table_Ptr, Old_Alloc_Ptr);
subtype Alloc_Type is
Table_Type (First .. First + New_Allocated_Length - 1);
type Alloc_Ptr is access all Alloc_Type;
function To_Table_Ptr is
new Ada.Unchecked_Conversion (Alloc_Ptr, Table_Ptr);
Old_Table : Old_Alloc_Ptr := To_Old_Alloc_Ptr (T.Table);
New_Table : constant Alloc_Ptr := new Alloc_Type;
begin
if T.Table /= Empty then
New_Table (First .. T.P.Last) := Old_Table (First .. T.P.Last);
Free (Old_Table);
end if;
T.Table := To_Table_Ptr (New_Table);
end;
pragma Assert (New_Last <= T.P.Last_Allocated);
pragma Assert (T.Table /= null);
pragma Assert (T.Table /= Empty);
end Grow;
--------------------
-- Increment_Last --
--------------------
procedure Increment_Last (T : in out Instance) is
begin
T.P.Last_Val := T.P.Last_Val + 1;
if T.P.Last_Val > T.P.Max then
Reallocate (T);
end if;
Allocate (T, 1);
end Increment_Last;
----------
@ -144,100 +237,57 @@ package body GNAT.Dynamic_Tables is
----------
procedure Init (T : in out Instance) is
Old_Length : constant Integer := T.P.Length;
begin
T.P.Last_Val := Min - 1;
T.P.Max := Min + Table_Initial - 1;
T.P.Length := T.P.Max - Min + 1;
-- If table is same size as before (happens when table is never
-- expanded which is a common case), then simply reuse it. Note
-- that this also means that an explicit Init call right after
-- the implicit one in the package body is harmless.
if Old_Length = T.P.Length then
return;
-- Otherwise we can use Reallocate to get a table of the right size.
-- Note that Reallocate works fine to allocate a table of the right
-- initial size when it is first allocated.
else
Reallocate (T);
end if;
Free (T);
end Init;
----------
-- Last --
----------
function Last (T : Instance) return Table_Index_Type is
function Last (T : Instance) return Table_Count_Type is
begin
return Table_Index_Type (T.P.Last_Val);
return T.P.Last;
end Last;
----------------
-- Reallocate --
----------------
procedure Reallocate (T : in out Instance) is
New_Length : Integer;
New_Size : size_t;
begin
if T.P.Max < T.P.Last_Val then
-- Now increment table length until it is sufficiently large. Use
-- the increment value or 10, which ever is larger (the reason
-- for the use of 10 here is to ensure that the table does really
-- increase in size (which would not be the case for a table of
-- length 10 increased by 3% for instance). Do the intermediate
-- calculation in Long_Long_Integer to avoid overflow.
while T.P.Max < T.P.Last_Val loop
New_Length :=
Integer
(Long_Long_Integer (T.P.Length) *
(100 + Long_Long_Integer (Table_Increment)) / 100);
if New_Length > T.P.Length then
T.P.Length := New_Length;
else
T.P.Length := T.P.Length + 10;
end if;
T.P.Max := Min + T.P.Length - 1;
end loop;
end if;
New_Size :=
size_t ((T.P.Max - Min + 1) *
(Table_Type'Component_Size / Storage_Unit));
if T.Table = null then
T.Table := To_Pointer (Alloc (New_Size));
elsif New_Size > 0 then
T.Table :=
To_Pointer (Realloc (Ptr => To_Address (T.Table),
Size => New_Size));
end if;
if T.P.Length /= 0 and then T.Table = null then
raise Storage_Error;
end if;
end Reallocate;
-------------
-- Release --
-------------
procedure Release (T : in out Instance) is
pragma Assert (not T.Locked);
Old_Last_Allocated : constant Table_Count_Type := T.P.Last_Allocated;
begin
T.P.Length := T.P.Last_Val - Integer (Table_Low_Bound) + 1;
T.P.Max := T.P.Last_Val;
Reallocate (T);
if T.P.Last /= T.P.Last_Allocated then
pragma Assert (T.P.Last < T.P.Last_Allocated);
pragma Assert (T.Table /= Empty);
declare
subtype Old_Alloc_Type is Table_Type (First .. Old_Last_Allocated);
type Old_Alloc_Ptr is access all Old_Alloc_Type;
procedure Free is
new Ada.Unchecked_Deallocation (Old_Alloc_Type, Old_Alloc_Ptr);
function To_Old_Alloc_Ptr is
new Ada.Unchecked_Conversion (Table_Ptr, Old_Alloc_Ptr);
subtype Alloc_Type is
Table_Type (First .. First + T.P.Last - 1);
type Alloc_Ptr is access all Alloc_Type;
function To_Table_Ptr is
new Ada.Unchecked_Conversion (Alloc_Ptr, Table_Ptr);
Old_Table : Old_Alloc_Ptr := To_Old_Alloc_Ptr (T.Table);
New_Table : constant Alloc_Ptr := new Alloc_Type'(Old_Table.all);
begin
T.P.Last_Allocated := T.P.Last;
Free (Old_Table);
T.Table := To_Table_Ptr (New_Table);
end;
end if;
pragma Assert (T.P.Last = T.P.Last_Allocated);
end Release;
--------------
@ -245,60 +295,18 @@ package body GNAT.Dynamic_Tables is
--------------
procedure Set_Item
(T : in out Instance;
Index : Table_Index_Type;
Item : Table_Component_Type)
(T : in out Instance;
Index : Valid_Table_Index_Type;
Item : Table_Component_Type)
is
-- If Item is a value within the current allocation, and we are going to
-- reallocate, then we must preserve an intermediate copy here before
-- calling Increment_Last. Otherwise, if Table_Component_Type is passed
-- by reference, we are going to end up copying from storage that might
-- have been deallocated from Increment_Last calling Reallocate.
subtype Allocated_Table_T is
Table_Type (T.Table'First .. Table_Index_Type (T.P.Max + 1));
-- A constrained table subtype one element larger than the currently
-- allocated table.
Allocated_Table_Address : constant System.Address :=
T.Table.all'Address;
-- Used for address clause below (we can't use non-static expression
-- Table.all'Address directly in the clause because some older versions
-- of the compiler do not allow it).
Allocated_Table : Allocated_Table_T;
pragma Import (Ada, Allocated_Table);
pragma Suppress (Range_Check, On => Allocated_Table);
for Allocated_Table'Address use Allocated_Table_Address;
-- Allocated_Table represents the currently allocated array, plus one
-- element (the supplementary element is used to have a convenient way
-- to the address just past the end of the current allocation). Range
-- checks are suppressed because this unit uses direct calls to
-- System.Memory for allocation, and this can yield misaligned storage
-- (and we cannot rely on the bootstrap compiler supporting specifically
-- disabling alignment checks, so we need to suppress all range checks).
-- It is safe to suppress this check here because we know that a
-- (possibly misaligned) object of that type does actually exist at that
-- address.
-- ??? We should really improve the allocation circuitry here to
-- guarantee proper alignment.
Need_Realloc : constant Boolean := Integer (Index) > T.P.Max;
-- True if this operation requires storage reallocation (which may
-- involve moving table contents around).
Item_Copy : constant Table_Component_Type := Item;
begin
-- If we're going to reallocate, check whether Item references an
-- element of the currently allocated table.
if Need_Realloc
and then Allocated_Table'Address <= Item'Address
and then Item'Address <
Allocated_Table (Table_Index_Type (T.P.Max + 1))'Address
then
-- If so, save a copy on the stack because Increment_Last will
-- reallocate storage and might deallocate the current table.
-- If Set_Last is going to reallocate the table, we make a copy of Item,
-- in case the call was "Set_Item (T, X, T.Table (Y));", and Item is
-- passed by reference. Without the copy, we would deallocate the array
-- containing Item, leaving a dangling pointer.
if Index > T.P.Last_Allocated then
declare
Item_Copy : constant Table_Component_Type := Item;
begin
@ -306,34 +314,28 @@ package body GNAT.Dynamic_Tables is
T.Table (Index) := Item_Copy;
end;
else
-- Here we know that either we won't reallocate (case of Index < Max)
-- or that Item is not in the currently allocated table.
if Integer (Index) > T.P.Last_Val then
Set_Last (T, Index);
end if;
T.Table (Index) := Item;
return;
end if;
if Index > T.P.Last then
Set_Last (T, Index);
end if;
T.Table (Index) := Item_Copy;
end Set_Item;
--------------
-- Set_Last --
--------------
procedure Set_Last (T : in out Instance; New_Val : Table_Index_Type) is
procedure Set_Last (T : in out Instance; New_Val : Table_Count_Type) is
pragma Assert (not T.Locked);
begin
if Integer (New_Val) < T.P.Last_Val then
T.P.Last_Val := Integer (New_Val);
else
T.P.Last_Val := Integer (New_Val);
if T.P.Last_Val > T.P.Max then
Reallocate (T);
end if;
if New_Val > T.P.Last_Allocated then
Grow (T, New_Val);
end if;
T.P.Last := New_Val;
end Set_Last;
----------------
@ -341,13 +343,12 @@ package body GNAT.Dynamic_Tables is
----------------
procedure Sort_Table (Table : in out Instance) is
Temp : Table_Component_Type;
-- A temporary position to simulate index 0
-- Local subprograms
function Index_Of (Idx : Natural) return Table_Index_Type;
function Index_Of (Idx : Natural) return Table_Index_Type'Base;
-- Return index of Idx'th element of table
function Lower_Than (Op1, Op2 : Natural) return Boolean;
@ -362,11 +363,11 @@ package body GNAT.Dynamic_Tables is
-- Index_Of --
--------------
function Index_Of (Idx : Natural) return Table_Index_Type is
function Index_Of (Idx : Natural) return Table_Index_Type'Base is
J : constant Integer'Base :=
Table_Index_Type'Pos (First) + Idx - 1;
Table_Index_Type'Base'Pos (First) + Idx - 1;
begin
return Table_Index_Type'Val (J);
return Table_Index_Type'Base'Val (J);
end Index_Of;
----------
@ -401,8 +402,7 @@ package body GNAT.Dynamic_Tables is
else
return
Lt (Table.Table (Index_Of (Op1)),
Table.Table (Index_Of (Op2)));
Lt (Table.Table (Index_Of (Op1)), Table.Table (Index_Of (Op2)));
end if;
end Lower_Than;

View File

@ -6,7 +6,7 @@
-- --
-- S p e c --
-- --
-- Copyright (C) 2000-2015, AdaCore --
-- Copyright (C) 2000-2016, AdaCore --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
@ -41,40 +41,49 @@
-- instances of the table, while an instantiation of GNAT.Table creates a
-- single instance of the table type.
-- Note that this interface should remain synchronized with those in
-- GNAT.Table and the GNAT compiler source unit Table to keep as much
-- coherency as possible between these three related units.
-- Note that these three interfaces should remain synchronized to keep as much
-- coherency as possible among these three related units:
--
-- GNAT.Dynamic_Tables
-- GNAT.Table
-- Table (the compiler unit)
pragma Compiler_Unit_Warning;
with Ada.Unchecked_Conversion;
generic
type Table_Component_Type is private;
type Table_Index_Type is range <>;
Table_Low_Bound : Table_Index_Type;
Table_Initial : Positive;
Table_Increment : Natural;
Table_Initial : Positive := 8;
Table_Increment : Natural := 100;
package GNAT.Dynamic_Tables is
-- Table_Component_Type and Table_Index_Type specify the type of the
-- array, Table_Low_Bound is the lower bound. Table_Index_Type must be an
-- integer type. The effect is roughly to declare:
-- Table_Component_Type and Table_Index_Type specify the type of the array,
-- Table_Low_Bound is the lower bound. The effect is roughly to declare:
-- Table : array (Table_Low_Bound .. <>) of Table_Component_Type;
-- Note: since the upper bound can be one less than the lower
-- bound for an empty array, the table index type must be able
-- to cover this range, e.g. if the lower bound is 1, then the
-- Table_Index_Type should be Natural rather than Positive.
-- The lower bound of Table_Index_Type is ignored.
-- Table_Component_Type may be any Ada type, except that controlled
-- types are not supported. Note however that default initialization
-- will NOT occur for array components.
pragma Assert (Table_Low_Bound /= Table_Index_Type'Base'First);
-- The Table_Initial values controls the allocation of the table when
-- it is first allocated, either by default, or by an explicit Init
-- call.
function First return Table_Index_Type;
pragma Inline (First);
-- Export First as synonym for Table_Low_Bound (parallel with use of Last)
subtype Valid_Table_Index_Type is Table_Index_Type'Base
range Table_Low_Bound .. Table_Index_Type'Base'Last;
subtype Table_Count_Type is Table_Index_Type'Base
range Table_Low_Bound - 1 .. Table_Index_Type'Base'Last;
-- Table_Component_Type must not be a type with controlled parts.
-- The Table_Initial value controls the allocation of the table when
-- it is first allocated.
-- The Table_Increment value controls the amount of increase, if the
-- table has to be increased in size. The value given is a percentage
@ -90,97 +99,114 @@ package GNAT.Dynamic_Tables is
-- to take the access of a table element, use Unrestricted_Access.
type Table_Type is
array (Table_Index_Type range <>) of Table_Component_Type;
array (Valid_Table_Index_Type range <>) of Table_Component_Type;
subtype Big_Table_Type is
Table_Type (Table_Low_Bound .. Table_Index_Type'Last);
Table_Type (Table_Low_Bound .. Valid_Table_Index_Type'Last);
-- We work with pointers to a bogus array type that is constrained with
-- the maximum possible range bound. This means that the pointer is a thin
-- pointer, which is more efficient. Since subscript checks in any case
-- must be on the logical, rather than physical bounds, safety is not
-- compromised by this approach. These types should not be used by the
-- client.
-- compromised by this approach.
-- To get subscript checking, rename a slice of the Table, like this:
-- Table : Table_Type renames T.Table (First .. Last (T));
-- and the refer to components of Table.
type Table_Ptr is access all Big_Table_Type;
for Table_Ptr'Storage_Size use 0;
-- The table is actually represented as a pointer to allow reallocation.
-- This type should not be used by the client.
-- The table is actually represented as a pointer to allow reallocation
type Table_Private is private;
-- Table private data that is not exported in Instance
-- Private use only:
subtype Empty_Table_Array_Type is
Table_Type (Table_Low_Bound .. Table_Low_Bound - 1);
type Empty_Table_Array_Ptr is access all Empty_Table_Array_Type;
Empty_Table_Array : aliased Empty_Table_Array_Type;
function Empty_Table_Array_Ptr_To_Table_Ptr is
new Ada.Unchecked_Conversion (Empty_Table_Array_Ptr, Table_Ptr);
-- End private use only. The above are used to initialize Table to point to
-- an empty array.
type Instance is record
Table : aliased Table_Ptr := null;
-- The table itself. The lower bound is the value of Low_Bound.
-- Logically the upper bound is the current value of Last (although
-- the actual size of the allocated table may be larger than this).
-- The program may only access and modify Table entries in the
-- range First .. Last.
Table : aliased Table_Ptr :=
Empty_Table_Array_Ptr_To_Table_Ptr (Empty_Table_Array'Access);
-- The table itself. The lower bound is the value of First. Logically
-- the upper bound is the current value of Last (although the actual
-- size of the allocated table may be larger than this). The program may
-- only access and modify Table entries in the range First .. Last.
--
-- It's a good idea to access this via a renaming of a slice, in order
-- to ensure bounds checking, as in:
--
-- Tab : Table_Type renames X.Table (First .. X.Last);
Locked : Boolean := False;
-- Table expansion is permitted only if this switch is set to False. A
-- client may set Locked to True, in which case any attempt to expand
-- the table will cause an assertion failure. Note that while a table
-- is locked, its address in memory remains fixed and unchanging.
P : Table_Private;
end record;
procedure Init (T : in out Instance);
-- This procedure allocates a new table of size Initial (freeing any
-- previously allocated larger table). Init must be called before using
-- the table. Init is convenient in reestablishing a table for new use.
-- Reinitializes the table to empty. There is no need to call this before
-- using a table; tables default to empty.
function Last (T : Instance) return Table_Index_Type;
function Last (T : Instance) return Table_Count_Type;
pragma Inline (Last);
-- Returns the current value of the last used entry in the table,
-- which can then be used as a subscript for Table. Note that the
-- only way to modify Last is to call the Set_Last procedure. Last
-- must always be used to determine the logically last entry.
-- Returns the current value of the last used entry in the table, which can
-- then be used as a subscript for Table.
procedure Release (T : in out Instance);
-- Storage is allocated in chunks according to the values given in the
-- Initial and Increment parameters. A call to Release releases all
-- storage that is allocated, but is not logically part of the current
-- Table_Initial and Table_Increment parameters. A call to Release releases
-- all storage that is allocated, but is not logically part of the current
-- array value. Current array values are not affected by this call.
procedure Free (T : in out Instance);
-- Free all allocated memory for the table. A call to init is required
-- before any use of this table after calling Free.
-- Same as Init
First : constant Table_Index_Type := Table_Low_Bound;
-- Export First as synonym for Low_Bound (parallel with use of Last)
procedure Set_Last (T : in out Instance; New_Val : Table_Index_Type);
procedure Set_Last (T : in out Instance; New_Val : Table_Count_Type);
pragma Inline (Set_Last);
-- This procedure sets Last to the indicated value. If necessary the
-- table is reallocated to accommodate the new value (i.e. on return
-- the allocated table has an upper bound of at least Last). If
-- Set_Last reduces the size of the table, then logically entries are
-- removed from the table. If Set_Last increases the size of the
-- table, then new entries are logically added to the table.
-- This procedure sets Last to the indicated value. If necessary the table
-- is reallocated to accommodate the new value (i.e. on return the
-- allocated table has an upper bound of at least Last). If Set_Last
-- reduces the size of the table, then logically entries are removed from
-- the table. If Set_Last increases the size of the table, then new entries
-- are logically added to the table.
procedure Increment_Last (T : in out Instance);
pragma Inline (Increment_Last);
-- Adds 1 to Last (same as Set_Last (Last + 1)
-- Adds 1 to Last (same as Set_Last (Last + 1))
procedure Decrement_Last (T : in out Instance);
pragma Inline (Decrement_Last);
-- Subtracts 1 from Last (same as Set_Last (Last - 1)
-- Subtracts 1 from Last (same as Set_Last (Last - 1))
procedure Append (T : in out Instance; New_Val : Table_Component_Type);
pragma Inline (Append);
-- Appends New_Val onto the end of the table
-- Equivalent to:
-- Increment_Last (T);
-- T.Table (T.Last) := New_Val;
-- i.e. the table size is increased by one, and the given new item
-- stored in the newly created table element.
procedure Append_All (T : in out Instance; New_Vals : Table_Type);
-- Appends all components of New_Vals
procedure Set_Item
(T : in out Instance;
Index : Table_Index_Type;
Index : Valid_Table_Index_Type;
Item : Table_Component_Type);
pragma Inline (Set_Item);
-- Put Item in the table at position Index. The table is expanded if
-- current table length is less than Index and in that case Last is set to
-- Index. Item will replace any value already present in the table at this
-- position.
-- Put Item in the table at position Index. If Index points to an existing
-- item (i.e. it is in the range First .. Last (T)), the item is replaced.
-- Otherwise (i.e. Index > Last (T), the table is expanded, and Last is set
-- to Index.
procedure Allocate (T : in out Instance; Num : Integer := 1);
pragma Inline (Allocate);
@ -188,17 +214,17 @@ package GNAT.Dynamic_Tables is
generic
with procedure Action
(Index : Table_Index_Type;
(Index : Valid_Table_Index_Type;
Item : Table_Component_Type;
Quit : in out Boolean) is <>;
procedure For_Each (Table : Instance);
-- Calls procedure Action for each component of the table Table, or until
-- one of these calls set Quit to True.
-- Calls procedure Action for each component of the table, or until one of
-- these calls set Quit to True.
generic
with function Lt (Comp1, Comp2 : Table_Component_Type) return Boolean;
procedure Sort_Table (Table : in out Instance);
-- This procedure sorts the components of table Table into ascending
-- This procedure sorts the components of the table into ascending
-- order making calls to Lt to do required comparisons, and using
-- assignments to move components around. The Lt function returns True
-- if Comp1 is less than Comp2 (in the sense of the desired sort), and
@ -208,16 +234,16 @@ package GNAT.Dynamic_Tables is
-- in the table is not preserved).
private
type Table_Private is record
Max : Integer;
-- Subscript of the maximum entry in the currently allocated table
Last_Allocated : Table_Count_Type := Table_Low_Bound - 1;
-- Subscript of the maximum entry in the currently allocated table.
-- Initial value ensures that we initially allocate the table.
Length : Integer := 0;
-- Number of entries in currently allocated table. The value of zero
-- ensures that we initially allocate the table.
Last : Table_Count_Type := Table_Low_Bound - 1;
-- Current value of Last function
Last_Val : Integer;
-- Current value of Last
-- Invariant: Last <= Last_Allocated
end record;
end GNAT.Dynamic_Tables;

View File

@ -68,6 +68,7 @@ with Stand; use Stand;
with Sinfo; use Sinfo;
with Sinput; use Sinput;
with System;
with System.CRC32; use System.CRC32;
with Stringt; use Stringt;
with Style;
with Stylesw; use Stylesw;
@ -6139,37 +6140,142 @@ package body Sem_Attr is
Check_E0;
Check_Type;
-- This processing belongs in Eval_Attribute ???
declare
function Type_Key return String_Id;
-- A very preliminary implementation. For now, a signature
-- consists of only the type name. This is clearly incomplete
-- (e.g., adding a new field to a record type should change the
-- type's Type_Key attribute).
Full_Name : constant String_Id :=
Fully_Qualified_Name_String (Entity (P));
Deref : Boolean;
-- To simplify the handling of mutually recursive types, follow
-- a single dereference link in a composite type.
CRC : CRC32;
-- The computed signature for the type.
procedure Compute_Type_Key (T : Entity_Id);
-- Create a CRC integer from the declaration of the type, For
-- a composite type, fold in the representation of its components
-- in recursive fashion. We use directly the source representation
-- of the types involved.
--------------
-- Type_Key --
--------------
function Type_Key return String_Id is
Full_Name : constant String_Id :=
Fully_Qualified_Name_String (Entity (P));
procedure Compute_Type_Key (T : Entity_Id) is
SFI : Source_File_Index;
Buffer : Source_Buffer_Ptr;
P_Min, P_Max : Source_Ptr;
Rep : Node_Id;
begin
-- Copy all characters in Full_Name but the trailing NUL
procedure Process_One_Declaration;
-- Update CRC with the characters of one type declaration,
-- or a representation pragma that applies to the type.
Start_String;
for J in 1 .. String_Length (Full_Name) - 1 loop
Store_String_Char (Get_String_Char (Full_Name, Pos (J)));
-----------------------------
-- Process_One_Declaration --
-----------------------------
procedure Process_One_Declaration is
Ptr : Source_Ptr;
begin
Ptr := P_Min;
-- Scan type declaration, skipping blanks,
while Ptr <= P_Max loop
if Buffer (Ptr) /= ' ' then
System.CRC32.Update (CRC, Buffer (Ptr));
end if;
Ptr := Ptr + 1;
end loop;
end Process_One_Declaration;
begin -- Start of processing for Compute_Type_Key
if Is_Itype (T) then
return;
end if;
Sloc_Range (Enclosing_Declaration (T), P_Min, P_Max);
SFI := Get_Source_File_Index (P_Min);
Buffer := Source_Text (SFI);
Process_One_Declaration;
-- Recurse on relevant component types.
if Is_Array_Type (T) then
Compute_Type_Key (Component_Type (T));
elsif Is_Access_Type (T) then
if not Deref then
Deref := True;
Compute_Type_Key (Designated_Type (T));
end if;
elsif Is_Derived_Type (T) then
Compute_Type_Key (Etype (T));
elsif Is_Record_Type (T) then
declare
Comp : Entity_Id;
begin
Comp := First_Component (T);
while Present (Comp) loop
Compute_Type_Key (Etype (Comp));
Next_Component (Comp);
end loop;
end;
end if;
-- Fold in representation aspects for the type, which
-- appear in the same source buffer.
Rep := First_Rep_Item (T);
while Present (Rep) loop
if Comes_From_Source (Rep) then
Sloc_Range (Rep, P_Min, P_Max);
Process_One_Declaration;
end if;
Rep := Next_Rep_Item (Rep);
end loop;
Store_String_Chars ("'Type_Key");
return End_String;
end Type_Key;
end Compute_Type_Key;
begin
Rewrite (N, Make_String_Literal (Loc, Type_Key));
Start_String;
Deref := False;
-- Copy all characters in Full_Name but the trailing NUL
for J in 1 .. String_Length (Full_Name) - 1 loop
Store_String_Char (Get_String_Char (Full_Name, Pos (J)));
end loop;
-- For standard type return the name of the type. as there is
-- no explicit source declaration to use. Otherwise compute
-- CRC and convert it to string one character at a time. so as
-- not to use Image within the compiler.
if Scope (Entity (P)) /= Standard_Standard then
Initialize (CRC);
Compute_Type_Key (Entity (P));
if not Is_Frozen (Entity (P)) then
Error_Msg_N ("premature usage of Type_Key?", N);
end if;
while CRC > 0 loop
Store_String_Char (Character'Val (48 + (CRC rem 10)));
CRC := CRC / 10;
end loop;
end if;
Rewrite (N, Make_String_Literal (Loc, End_String));
end;
Analyze_And_Resolve (N, Standard_String);

View File

@ -114,10 +114,12 @@ package body Sem_Case is
Others_Present : Boolean;
Case_Node : Node_Id)
is
Predicate_Error : Boolean;
Predicate_Error : Boolean := False;
-- Flag to prevent cascaded errors when a static predicate is known to
-- be violated by one choice.
Num_Choices : constant Nat := Choice_Table'Last;
procedure Check_Against_Predicate
(Pred : in out Node_Id;
Choice : Choice_Bounds;
@ -130,6 +132,10 @@ package body Sem_Case is
-- choice that covered a predicate set. Error denotes whether the check
-- found an illegal intersection.
procedure Check_Duplicates;
-- Check for duplicate choices, and call Dup_Choice is there are any
-- such errors. Note that predicates are irrelevant here.
procedure Dup_Choice (Lo, Hi : Uint; C : Node_Id);
-- Post message "duplication of choice value(s) bla bla at xx". Message
-- is posted at location C. Caller sets Error_Msg_Sloc for xx.
@ -236,8 +242,7 @@ package body Sem_Case is
Val : Uint) return Boolean
is
begin
return
Val = Lo or else Val = Hi or else (Lo < Val and then Val < Hi);
return Lo <= Val and then Val <= Hi;
end Inside_Range;
-- Local variables
@ -276,14 +281,12 @@ package body Sem_Case is
return;
end if;
-- Step 1: Detect duplicate choices
-- Step 1: Ignore duplicate choices, other than to set the flag,
-- because these were already detected by Check_Duplicates.
if Inside_Range (Choice_Lo, Choice_Hi, Prev_Lo) then
Dup_Choice (Prev_Lo, UI_Min (Prev_Hi, Choice_Hi), LocN);
Error := True;
elsif Inside_Range (Choice_Lo, Choice_Hi, Prev_Hi) then
Dup_Choice (UI_Max (Choice_Lo, Prev_Lo), Prev_Hi, LocN);
if Inside_Range (Choice_Lo, Choice_Hi, Prev_Lo)
or else Inside_Range (Choice_Lo, Choice_Hi, Prev_Hi)
then
Error := True;
-- Step 2: Detect full coverage
@ -447,6 +450,59 @@ package body Sem_Case is
end if;
end Check_Against_Predicate;
----------------------
-- Check_Duplicates --
----------------------
procedure Check_Duplicates is
Prev_Hi : Uint := Expr_Value (Choice_Table (1).Hi);
begin
for Outer_Index in 2 .. Num_Choices loop
declare
Choice_Lo : constant Uint :=
Expr_Value (Choice_Table (Outer_Index).Lo);
Choice_Hi : constant Uint :=
Expr_Value (Choice_Table (Outer_Index).Hi);
begin
if Choice_Lo <= Prev_Hi then
-- Choices overlap; this is an error
declare
Choice : constant Node_Id :=
Choice_Table (Outer_Index).Node;
Prev_Choice : Node_Id;
begin
-- Find first previous choice that overlaps
for Inner_Index in 1 .. Outer_Index - 1 loop
if Choice_Lo <=
Expr_Value (Choice_Table (Inner_Index).Hi)
then
Prev_Choice := Choice_Table (Inner_Index).Node;
exit;
end if;
end loop;
if Sloc (Prev_Choice) <= Sloc (Choice) then
Error_Msg_Sloc := Sloc (Prev_Choice);
Dup_Choice
(Choice_Lo, UI_Min (Choice_Hi, Prev_Hi), Choice);
else
Error_Msg_Sloc := Sloc (Choice);
Dup_Choice
(Choice_Lo, UI_Min (Choice_Hi, Prev_Hi),
Prev_Choice);
end if;
end;
end if;
if Choice_Hi > Prev_Hi then
Prev_Hi := Choice_Hi;
end if;
end;
end loop;
end Check_Duplicates;
----------------
-- Dup_Choice --
----------------
@ -709,17 +765,13 @@ package body Sem_Case is
Bounds_Hi : constant Node_Id := Type_High_Bound (Bounds_Type);
Bounds_Lo : constant Node_Id := Type_Low_Bound (Bounds_Type);
Num_Choices : constant Nat := Choice_Table'Last;
Has_Predicate : constant Boolean :=
Is_OK_Static_Subtype (Bounds_Type)
and then Has_Static_Predicate (Bounds_Type);
Choice : Node_Id;
Choice_Hi : Uint;
Choice_Lo : Uint;
Error : Boolean;
Pred : Node_Id;
Prev_Choice : Node_Id;
Prev_Lo : Uint;
Prev_Hi : Uint;
@ -735,8 +787,6 @@ package body Sem_Case is
return;
end if;
Predicate_Error := False;
-- Choice_Table must start at 0 which is an unused location used by the
-- sorting algorithm. However the first valid position for a discrete
-- choice is 1.
@ -756,16 +806,22 @@ package body Sem_Case is
Sorting.Sort (Positive (Choice_Table'Last));
-- The type covered by the list of choices is actually a static subtype
-- subject to a static predicate. The predicate defines subsets of legal
-- values and requires finer grained analysis.
-- First check for duplicates. This involved the choices; predicates, if
-- any, are irrelevant.
Check_Duplicates;
-- Then check for overlaps
-- If the subtype has a static predicate, the predicate defines subsets
-- of legal values and requires finer grained analysis.
-- Note that in GNAT the predicate is considered static if the predicate
-- expression is static, independently of whether the aspect mentions
-- Static explicitly.
if Has_Predicate then
Pred := First (Static_Discrete_Predicate (Bounds_Type));
Pred := First (Static_Discrete_Predicate (Bounds_Type));
-- Make initial value smaller than 'First of type, so that first
-- range comparison succeeds. This applies both to integer types
@ -774,28 +830,30 @@ package body Sem_Case is
Prev_Lo := Expr_Value (Type_Low_Bound (Bounds_Type)) - 1;
Prev_Hi := Prev_Lo;
Error := False;
declare
Error : Boolean := False;
begin
for Index in 1 .. Num_Choices loop
Check_Against_Predicate
(Pred => Pred,
Choice => Choice_Table (Index),
Prev_Lo => Prev_Lo,
Prev_Hi => Prev_Hi,
Error => Error);
for Index in 1 .. Num_Choices loop
Check_Against_Predicate
(Pred => Pred,
Choice => Choice_Table (Index),
Prev_Lo => Prev_Lo,
Prev_Hi => Prev_Hi,
Error => Error);
-- The analysis detected an illegal intersection between a
-- choice and a static predicate set. Do not examine other
-- choices unless all errors are requested.
-- The analysis detected an illegal intersection between a choice
-- and a static predicate set. Do not examine other choices unless
-- all errors are requested.
if Error then
Predicate_Error := True;
if Error then
Predicate_Error := True;
if not All_Errors_Mode then
return;
if not All_Errors_Mode then
return;
end if;
end if;
end if;
end loop;
end loop;
end;
if Predicate_Error then
return;
@ -826,35 +884,11 @@ package body Sem_Case is
end if;
end if;
for Outer_Index in 2 .. Num_Choices loop
Choice_Lo := Expr_Value (Choice_Table (Outer_Index).Lo);
Choice_Hi := Expr_Value (Choice_Table (Outer_Index).Hi);
for Index in 2 .. Num_Choices loop
Choice_Lo := Expr_Value (Choice_Table (Index).Lo);
Choice_Hi := Expr_Value (Choice_Table (Index).Hi);
if Choice_Lo <= Prev_Hi then
Choice := Choice_Table (Outer_Index).Node;
-- Find first previous choice that overlaps
for Inner_Index in 1 .. Outer_Index - 1 loop
if Choice_Lo <=
Expr_Value (Choice_Table (Inner_Index).Hi)
then
Prev_Choice := Choice_Table (Inner_Index).Node;
exit;
end if;
end loop;
if Sloc (Prev_Choice) <= Sloc (Choice) then
Error_Msg_Sloc := Sloc (Prev_Choice);
Dup_Choice
(Choice_Lo, UI_Min (Choice_Hi, Prev_Hi), Choice);
else
Error_Msg_Sloc := Sloc (Choice);
Dup_Choice
(Choice_Lo, UI_Min (Choice_Hi, Prev_Hi), Prev_Choice);
end if;
elsif not Others_Present and then Choice_Lo /= Prev_Hi + 1 then
if Choice_Lo > Prev_Hi + 1 and then not Others_Present then
Missing_Choice (Prev_Hi + 1, Choice_Lo - 1);
end if;

View File

@ -401,8 +401,9 @@ package body Xref_Lib is
(File : ALI_File;
Num : Positive) return File_Reference
is
Table : Table_Type renames File.Dep.Table (1 .. Last (File.Dep));
begin
return File.Dep.Table (Num);
return Table (Num);
end File_Name;
--------------------
@ -642,10 +643,15 @@ package body Xref_Lib is
Token := Gnatchop_Name + 1;
end if;
File.Dep.Table (Num_Dependencies) := Add_To_Xref_File
(Ali (File_Start .. File_End),
Gnatchop_File => Ali (Token .. Ptr - 1),
Gnatchop_Offset => Gnatchop_Offset);
declare
Table : Table_Type renames
File.Dep.Table (1 .. Last (File.Dep));
begin
Table (Num_Dependencies) := Add_To_Xref_File
(Ali (File_Start .. File_End),
Gnatchop_File => Ali (Token .. Ptr - 1),
Gnatchop_Offset => Gnatchop_Offset);
end;
elsif W_Lines and then Ali (Ptr) = 'W' then
@ -854,6 +860,8 @@ package body Xref_Lib is
Ptr := Ptr + 1;
end Skip_To_Matching_Closing_Bracket;
Table : Table_Type renames File.Dep.Table (1 .. Last (File.Dep));
-- Start of processing for Parse_Identifier_Info
begin
@ -976,9 +984,9 @@ package body Xref_Lib is
-- We don't have a unit number specified, so we set P_Eun to
-- the current unit.
for K in Dependencies_Tables.First .. Last (File.Dep) loop
for K in Table'Range loop
P_Eun := K;
exit when File.Dep.Table (K) = File_Ref;
exit when Table (K) = File_Ref;
end loop;
end if;
@ -1011,7 +1019,7 @@ package body Xref_Lib is
Symbol,
P_Line,
P_Column,
File.Dep.Table (P_Eun));
Table (P_Eun));
end if;
end;
end if;
@ -1029,7 +1037,7 @@ package body Xref_Lib is
Add_Entity
(Pattern,
Get_Symbol_Name (P_Eun, P_Line, P_Column)
& ':' & Get_Gnatchop_File (File.Dep.Table (P_Eun))
& ':' & Get_Gnatchop_File (Table (P_Eun))
& ':' & Get_Line (Get_Parent (Decl_Ref))
& ':' & Get_Column (Get_Parent (Decl_Ref)),
False);
@ -1080,11 +1088,10 @@ package body Xref_Lib is
if Wide_Search then
declare
File_Ref : File_Reference;
pragma Unreferenced (File_Ref);
File_Name : constant String := Get_Gnatchop_File (File.X_File);
Ignored : File_Reference;
begin
File_Ref := Add_To_Xref_File (ALI_File_Name (File_Name), False);
Ignored := Add_To_Xref_File (ALI_File_Name (File_Name), False);
end;
end if;
@ -1252,6 +1259,8 @@ package body Xref_Lib is
Ptr : Positive renames File.Current_Line;
File_Nr : Natural;
Table : Table_Type renames File.Dep.Table (1 .. Last (File.Dep));
begin
while Ali (Ptr) = 'X' loop
@ -1267,8 +1276,8 @@ package body Xref_Lib is
-- If the referenced file is unknown, we simply ignore it
if File_Nr in Dependencies_Tables.First .. Last (File.Dep) then
File.X_File := File.Dep.Table (File_Nr);
if File_Nr in Table'Range then
File.X_File := Table (File_Nr);
else
File.X_File := Empty_File;
end if;