home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
gnat-2.06-src.tgz
/
tar.out
/
fsf
/
gnat
/
ada
/
checks.adb
< prev
next >
Wrap
Text File
|
1996-09-28
|
28KB
|
815 lines
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- C H E C K S --
-- --
-- B o d y --
-- --
-- $Revision: 1.5 $ --
-- --
-- Copyright (c) 1992,1993,1994,1995 NYU, All Rights Reserved --
-- --
-- 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- --
-- ware Foundation; either version 2, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT 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 distributed with GNAT; see file COPYING. If not, write --
-- to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. --
-- --
------------------------------------------------------------------------------
with Atree; use Atree;
with Einfo; use Einfo;
with Exp_Util; use Exp_Util;
with Nlists; use Nlists;
with Nmake; use Nmake;
with Rtsfind; use Rtsfind;
with Sem; use Sem;
with Sem_Eval; use Sem_Eval;
with Sem_Res; use Sem_Res;
with Sem_Util; use Sem_Util;
with Sinfo; use Sinfo;
with Snames; use Snames;
with Stand; use Stand;
with Tbuild; use Tbuild;
with Ttypes; use Ttypes;
with Uintp; use Uintp;
with Urealp; use Urealp;
package body Checks is
------------------------------
-- Access_Checks_Suppressed --
------------------------------
function Access_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Access_Checks
or else (Present (E) and then Suppress_Access_Checks (E));
end Access_Checks_Suppressed;
-------------------------------------
-- Accessibility_Checks_Suppressed --
-------------------------------------
function Accessibility_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Accessibility_Checks
or else (Present (E) and then Suppress_Accessibility_Checks (E));
end Accessibility_Checks_Suppressed;
------------------------
-- Apply_Access_Check --
------------------------
procedure Apply_Access_Check (N : Node_Id; Typ : Entity_Id) is
begin
if not Access_Checks_Suppressed (Typ) then
Set_Do_Access_Check (N, True);
end if;
end Apply_Access_Check;
-------------------------------------
-- Apply_Arithmetic_Overflow_Check --
-------------------------------------
-- This routine is called only if the type is an integer type, and
-- a software arithmetic overflow check must be performed for op
-- (add, subtract, divide, multiply):
-- x op y
-- is expanded into
-- Typ (Checktyp (x) op Checktyp (y));
-- where Typ is the type of the original expression, and Checktyp is an
-- integer type of sufficient length to hold the largest possible result.
-- In the case where the check type exceeds the size of Long_Long_Integer,
-- we use a different approach, expanding to:
-- typ (xxx_With_Ovflo_Check (Integer_64 (x), Integer (y)))
-- where xxx is Add, Divide, Multiply or Subtract as appropriate
procedure Apply_Arithmetic_Overflow_Check (N : Node_Id) is
Loc : constant Source_Ptr := Sloc (N);
Typ : constant Entity_Id := Etype (N);
Rtyp : constant Entity_Id := Root_Type (Typ);
Siz : constant Int := UI_To_Int (Esize (Rtyp));
Dsiz : constant Int := Siz * 2;
Opnod : constant Node_Id := Relocate_Node (N);
Ctyp : Entity_Id;
Opnd : Node_Id;
Cent : RE_Id;
begin
-- Find check type if one exists
if Dsiz <= Standard_Integer_Size then
Ctyp := Standard_Integer;
elsif Dsiz <= Standard_Long_Long_Integer_Size then
Ctyp := Standard_Long_Long_Integer;
-- No check type exists, use runtime call
else
if Nkind (N) = N_Op_Add then
Cent := RE_Add_With_Ovflo_Check;
elsif Nkind (N) = N_Op_Divide then
Cent := RE_Divide_With_Ovflo_Check;
elsif Nkind (N) = N_Op_Multiply then
Cent := RE_Multiply_With_Ovflo_Check;
elsif Nkind (N) = N_Op_Subtract then
Cent := RE_Subtract_With_Ovflo_Check;
else
pragma Assert (False); null;
end if;
Rewrite_Substitute_Tree (N,
Make_Type_Conversion (Loc,
Subtype_Mark => New_Reference_To (Typ, Loc),
Expression =>
Make_Function_Call (Loc,
Name => New_Reference_To (RTE (Cent), Loc),
Parameter_Associations => New_List (
Make_Type_Conversion (Loc,
Subtype_Mark =>
New_Reference_To (RTE (RE_Integer_64), Loc),
Expression => Left_Opnd (Opnod)),
Make_Type_Conversion (Loc,
Subtype_Mark =>
New_Reference_To (RTE (RE_Integer_64), Loc),
Expression => Right_Opnd (Opnod))))));
Analyze (N);
Resolve (N, Typ);
return;
end if;
-- If we fall through, we have the case where we do the arithmetic in
-- the next higher type and get the check by conversion. In these cases
-- Ctyp is set to the type to be used as the check type.
Opnd :=
Make_Type_Conversion (Loc,
Subtype_Mark => New_Reference_To (Ctyp, Loc),
Expression => Left_Opnd (Opnod));
Analyze (Opnd);
Set_Etype (Opnd, Ctyp);
Set_Analyzed (Opnd, True);
Set_Left_Opnd (Opnod, Opnd);
Opnd :=
Make_Type_Conversion (Loc,
Subtype_Mark => New_Reference_To (Ctyp, Loc),
Expression => Right_Opnd (Opnod));
Analyze (Opnd);
Set_Etype (Opnd, Ctyp);
Set_Analyzed (Opnd, True);
Set_Right_Opnd (Opnod, Opnd);
-- The type of the operation changes to the base type of the check
-- type, and we reset the overflow check indication, since clearly
-- no overflow is possible now that we are using a double length
-- type. We also set the Analyzed flag to avoid a recursive attempt
-- to expand the node.
Set_Etype (Opnod, Base_Type (Ctyp));
Set_Do_Overflow_Check (Opnod, False);
Set_Analyzed (Opnod, True);
-- Now build the outer conversion
Opnd :=
Make_Type_Conversion (Loc,
Subtype_Mark => New_Reference_To (Typ, Loc),
Expression => Opnod);
Analyze (Opnd);
Set_Etype (Opnd, Typ);
Set_Analyzed (Opnd, True);
Set_Do_Overflow_Check (Opnd, True);
Rewrite_Substitute_Tree (N, Opnd);
end Apply_Arithmetic_Overflow_Check;
------------------------------
-- Apply_Discriminant_Check --
------------------------------
procedure Apply_Discriminant_Check (N : Node_Id; Typ : Entity_Id) is
begin
if not Discriminant_Checks_Suppressed (Typ) then
Set_Do_Discriminant_Check (N, True);
end if;
end Apply_Discriminant_Check;
------------------------
-- Apply_Length_Check --
------------------------
procedure Apply_Length_Check
(Expr : Node_Id;
Typ : Entity_Id)
is
Loc : constant Source_Ptr := Sloc (Expr);
Expr_Actual : constant Node_Id := Get_Referenced_Object (Expr);
Exptyp : constant Entity_Id := Get_Actual_Subtype (Expr_Actual);
Ndims : constant Nat := Number_Dimensions (Typ);
Cond : Node_Id;
function Get_Length
(E : Entity_Id;
Indx : Nat)
return Node_Id;
-- Returns expression for Indx'th length of array type E
function Get_Length
(E : Entity_Id;
Indx : Nat)
return Node_Id
is
N : Node_Id;
begin
if Ekind (E) = E_String_Literal_Subtype then
return
Make_Integer_Literal (Loc,
Intval => String_Literal_Length (E));
else
N :=
Make_Attribute_Reference (Loc,
Attribute_Name => Name_Length,
Prefix => New_Occurrence_Of (E, Loc));
if Indx > 1 then
Set_Expressions (N, New_List (
Make_Integer_Literal (Loc, UI_From_Int (Indx))));
end if;
return N;
end if;
end Get_Length;
-- Start processing for Length_Check
begin
-- String_Literal case. This needs to be handled specially because
-- no index types are available for string literals. The condition
-- is simply:
-- Typ'Length = string-literal-length
if Nkind (Expr_Actual) = N_String_Literal then
Cond :=
Make_Op_Ne (Loc,
Left_Opnd => Get_Length (Typ, 1),
Right_Opnd =>
Make_Integer_Literal (Loc,
Intval => String_Literal_Length (Etype (Expr_Actual))));
-- Handle cases where we do not get a usable actual subtype that is
-- constrained. This happens for example in the function call and
-- explicit dereference cases. In these cases, we have to get the
-- length from the expression itself, making sure we do not evaluate
-- it more than once.
-- Typ'Length /= Expr'Length (1) or else
-- Typ'Length (2) /= Expr'Length (2) or else
-- Typ'Length (3) /= Expr'Length (3) or else
-- ...
-- Here Expr is the original expression, or more properly the result
-- of applying Duplicate_Expr to the original tree, forcing the result
-- to be a name.
elsif not Is_Constrained (Exptyp) then
declare
Cond1 : Node_Id;
begin
-- Build the condition for the explicit dereference case
Cond := Empty;
for Indx in 1 .. Ndims loop
-- Build check for one index position
Cond1 :=
Make_Op_Ne (Loc,
Left_Opnd => Get_Length (Typ, Indx),
Right_Opnd =>
Make_Attribute_Reference (Loc,
Attribute_Name => Name_Length,
Prefix =>
Duplicate_Subexpr (Expr, Name_Req => True),
Expressions => New_List (
Make_Integer_Literal (Loc, UI_From_Int (Indx)))));
-- Add new check to evolving condition
if No (Cond) then
Cond := Cond1;
else
Cond :=
Make_Or_Else (Loc,
Left_Opnd => Cond,
Right_Opnd => Cond1);
end if;
end loop;
end;
-- General array case. Here we have a usable actual subtype for the
-- expression, and the condition is built from the two types:
-- Typ'Length /= Exptyp'Length or else
-- Typ'Length (2) /= Exptyp'Length (2) or else
-- Typ'Length (3) /= Exptyp'Length (3) or else
-- ...
-- The comparison for an individual index subtype is omitted if the
-- corresponding index subtypes statically match, since the result
-- is known to be true. Note that this test is worth while even though
-- we do static evaluation, because it is possible for non-static
-- subtypes to statically match.
else
declare
L_Index : Node_Id;
R_Index : Node_Id;
Cond1 : Node_Id;
begin
L_Index := First_Index (Typ);
R_Index := First_Index (Exptyp);
Cond := Empty;
for Indx in 1 .. Ndims loop
if not
Subtypes_Statically_Match (Etype (L_Index), Etype (R_Index))
then
Cond1 :=
Make_Op_Ne (Loc,
Left_Opnd => Get_Length (Typ, Indx),
Right_Opnd => Get_Length (Exptyp, Indx));
-- Add new check to evolving condition
if No (Cond) then
Cond := Cond1;
else
Cond :=
Make_Or_Else (Loc,
Left_Opnd => Cond,
Right_Opnd => Cond1);
end if;
end if;
L_Index := Next (L_Index);
R_Index := Next (R_Index);
end loop;
end;
end if;
-- Construct the test and insert into the tree
if Present (Cond) then
Insert_Action (Expr,
Make_If_Statement (Loc,
Condition => Cond,
Then_Statements => New_List (
Make_Raise_Statement (Loc,
Name =>
New_Reference_To
(Standard_Constraint_Error, Loc)))));
if Is_Entity_Name (Cond)
and then Entity (Cond) = Standard_True
then
Compile_Time_Constraint_Error (Expr, "wrong length for array?");
end if;
end if;
end Apply_Length_Check;
-----------------------
-- Apply_Range_Check --
-----------------------
-- A range constraint may be applied in any of the following contexts:
-- object declaration, subtype declaration, derived declaration
-- assignment, function/procedure/entry call, type conversion
-- Shouldn't this be part of the expander ???
procedure Apply_Range_Check
(N : Node_Id;
Source_Type : Entity_Id;
Target_Type : Entity_Id)
is
Checks_On : constant Boolean :=
not Index_Checks_Suppressed (Target_Type)
and not Range_Checks_Suppressed (Target_Type);
begin
-- Don't worry about range checks if we have a previous error or if
-- the expression is already signalled as raising a constraint error
-- which means that a warning message has already been posted.
if Source_Type = Any_Type
or else Target_Type = Any_Type
or else Raises_Constraint_Error (N)
then
return;
-- Confine the range checks currently to only scalar types
elsif not Is_Scalar_Type (Source_Type) then
return;
-- For now unconditionally do check if kinds of base types are
-- different, as happens in a conversion. We can still carry out
-- many of the optimizations, but they are more complex.
elsif
Ekind (Base_Type (Source_Type)) /= Ekind (Base_Type (Target_Type))
then
Set_Do_Range_Check (N, Checks_On);
-- For literals, we can tell if the constraint error will be raised
-- at compile time, so we never need a dynamic check, but if the
-- exception will be raised, then post the usual warning, and replace
-- the literal with a raise constraint error expression.
elsif Is_OK_Static_Expression (N) then
declare
LB : constant Node_Id := Type_Low_Bound (Target_Type);
UB : constant Node_Id := Type_High_Bound (Target_Type);
Out_Of_Range : Boolean;
Static_Bounds : constant Boolean :=
Is_OK_Static_Expression (LB)
and Is_OK_Static_Expression (UB);
begin
-- If literal is outside a static bound, raise the warning
-- Following range tests should use sem_eval routine ???
if Static_Bounds then
if Is_Floating_Point_Type (Source_Type) then
Out_Of_Range := (Expr_Value_R (N) < Expr_Value_R (LB))
or else
(Expr_Value_R (N) > Expr_Value_R (UB));
else -- fixed or discrete type
Out_Of_Range :=
Expr_Value (N) < Expr_Value (LB)
or else
Expr_Value (N) > Expr_Value (UB);
end if;
-- Bounds of the type are static and the literal is not
-- out of range so there is nothing to do.
if Out_Of_Range then
Compile_Time_Constraint_Error
(N, "static value out of range?");
end if;
-- Otherwise the check is needed
else
Set_Do_Range_Check (N, Checks_On);
end if;
end;
-- Here for the case of a non-static expression, we need a runtime
-- check unless the source type range is guaranteed to be in the
-- range of the target type.
else
if not In_Subrange_Of (Source_Type, Target_Type) then
Set_Do_Range_Check (N, Checks_On);
end if;
end if;
end Apply_Range_Check;
-----------------------------
-- Apply_Slice_Range_Check --
-----------------------------
procedure Apply_Slice_Range_Check
(N : Node_Id;
Source_Type : Entity_Id;
Target_Type : Entity_Id)
is
Checks_On : constant Boolean :=
not Index_Checks_Suppressed (Target_Type)
and not Range_Checks_Suppressed (Target_Type);
LB : Node_Id := Low_Bound (N);
HB : Node_Id := High_Bound (N);
Null_Range : Boolean;
begin
-- Don't worry about range checks if we have a previous error or if
-- the expression is already signalled as raising a constraint error
-- which means that a warning message has already been posted.
if Source_Type = Any_Type
or else Target_Type = Any_Type
or else Raises_Constraint_Error (N)
then
return;
-- Confine the range checks currently to only scalar types
elsif not Is_Scalar_Type (Source_Type) then
return;
elsif Is_OK_Static_Expression (LB)
and then Is_OK_Static_Expression (HB) then
if Is_Floating_Point_Type (Source_Type) then
Null_Range := Expr_Value_R (HB) < Expr_Value_R (LB);
else -- fixed or discrete type
Null_Range := Expr_Value (HB) < Expr_Value (LB);
end if;
if Null_Range then
return;
else
Apply_Range_Check (LB, Source_Type, Target_Type);
Apply_Range_Check (HB, Source_Type, Target_Type);
if Do_Range_Check (LB) then
Set_Do_Range_Check (N, Checks_On);
end if;
end if;
else
if not In_Subrange_Of (Source_Type, Target_Type) then
Set_Do_Range_Check (N, Checks_On);
end if;
end if;
end Apply_Slice_Range_Check;
-------------------------------
-- Apply_Static_Length_Check --
-------------------------------
procedure Apply_Static_Length_Check
(N : Node_Id;
Source_Type : Entity_Id;
Target_Type : Entity_Id)
is
Source_Index : Node_Id;
Target_Index : Node_Id;
S_Low : Node_Id;
S_High : Node_Id;
T_Low : Node_Id;
T_High : Node_Id;
S_Length : Uint;
T_Length : Uint;
begin
if not Is_Array_Type (Source_Type)
or else not Is_Array_Type (Target_Type)
then
return;
end if;
-- If the target two array type is unconstrained it will take
-- the bounds from the Source_Type, so the length check succeds
-- by definition. Incidentally we check also for unconstrained
-- Source_Type in the event the caller mixed them up.
if not Is_Constrained (Target_Type)
or else not Is_Constrained (Source_Type)
then
return;
end if;
Source_Index := First_Index (Source_Type);
Target_Index := First_Index (Target_Type);
while Present (Source_Index) and then Present (Target_Index) loop
if Nkind (Source_Index) = N_Raise_Constraint_Error
or else Nkind (Target_Index) = N_Raise_Constraint_Error
then
return;
end if;
Get_Index_Bounds (Source_Index, S_Low, S_High);
Get_Index_Bounds (Target_Index, T_Low, T_High);
if Nkind (S_Low) = N_Raise_Constraint_Error
or else Nkind (S_High) = N_Raise_Constraint_Error
or else Nkind (T_Low) = N_Raise_Constraint_Error
or else Nkind (T_High) = N_Raise_Constraint_Error
then
return;
end if;
if Is_Static_Expression (S_Low)
and then Is_Static_Expression (S_High)
and then Is_Static_Expression (T_Low)
and then Is_Static_Expression (T_High)
then
if Expr_Value (S_High) >= Expr_Value (S_Low) then
S_Length := Expr_Value (S_High) - Expr_Value (S_Low) + 1;
else
S_Length := UI_From_Int (0);
end if;
if Expr_Value (T_High) >= Expr_Value (T_Low) then
T_Length := Expr_Value (T_High) - Expr_Value (T_Low) + 1;
else
T_Length := UI_From_Int (0);
end if;
if S_Length < T_Length then
Compile_Time_Constraint_Error (N, "too few elements?");
return;
elsif S_Length > T_Length then
Compile_Time_Constraint_Error (N, "too many elements?");
return;
end if;
end if;
Source_Index := Next_Index (Source_Index);
Target_Index := Next_Index (Target_Index);
end loop;
end Apply_Static_Length_Check;
---------------------------------------
-- Apply_Subscript_Conversion_Checks --
---------------------------------------
procedure Apply_Subscript_Conversion_Checks (N : Node_Id) is
Prefix_Type : Entity_Id := Etype (Prefix (N));
Index : Entity_Id;
Expr : Node_Id;
begin
-- If all index checks are suppressed globally do not do unnecessary
-- tree constructions used only for subscript checking.
if Index_Checks_Suppressed (Empty) then
return;
end if;
if Is_Access_Type (Prefix_Type) then
Prefix_Type := Designated_Type (Prefix_Type);
end if;
-- Conversion checks need to be added only in the case of unconstrained
-- arrays or packed arrays since otherwise the appropriate array bounds
-- exist to make the index checks in appropriate calls to
-- Apply_Range_Check when resolving the indexed component.
if not (Is_Array_Type (Prefix_Type)
and then not Is_Constrained (Prefix_Type)
and then Ekind (Prefix_Type) /= E_Enum_Table_Type)
and then not Is_Packed (Prefix_Type)
then
return;
end if;
-- Transform indexed components of access types to a canonical form
-- using explicit .all notation so that getting the actual subtype
-- of the unconstrained type is made easier.
if Is_Access_Type (Etype (Prefix (N))) then
Rewrite_Substitute_Tree (N,
Make_Indexed_Component (Sloc (N),
Prefix => Make_Explicit_Dereference (Sloc (N),
Relocate_Node (Prefix (N))),
Expressions => Expressions (N)));
Analyze (N);
Resolve (N, Etype (Original_Node (N)));
return;
end if;
Index := First_Index (Get_Actual_Subtype (Prefix (N)));
Expr := First (Expressions (N));
-- For each subscript generate a type conversion to the corresponding
-- actual subtype for the index.
while Present (Index) loop
if not Index_Checks_Suppressed (Etype (Index)) then
Rewrite_Substitute_Tree (Expr, Convert_To (Etype (Index), Expr));
Analyze (Expr);
Resolve (Expr, Etype (Index));
end if;
Index := Next_Index (Index);
Expr := Next (Expr);
end loop;
end Apply_Subscript_Conversion_Checks;
------------------------------------
-- Discriminant_Checks_Suppressed --
------------------------------------
function Discriminant_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Discriminant_Checks
or else (Present (E) and then Suppress_Discriminant_Checks (E));
end Discriminant_Checks_Suppressed;
--------------------------------
-- Division_Checks_Suppressed --
--------------------------------
function Division_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Division_Checks
or else (Present (E) and then Suppress_Division_Checks (E));
end Division_Checks_Suppressed;
-----------------------------------
-- Elaboration_Checks_Suppressed --
-----------------------------------
function Elaboration_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Elaboration_Checks
or else (Present (E) and then Suppress_Elaboration_Checks (E));
end Elaboration_Checks_Suppressed;
-----------------------------
-- Index_Checks_Suppressed --
-----------------------------
function Index_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Index_Checks
or else (Present (E) and then Suppress_Index_Checks (E));
end Index_Checks_Suppressed;
------------------------------
-- Length_Checks_Suppressed --
------------------------------
function Length_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Length_Checks
or else (Present (E) and then Suppress_Length_Checks (E));
end Length_Checks_Suppressed;
--------------------------------
-- Overflow_Checks_Suppressed --
--------------------------------
function Overflow_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Overflow_Checks
or else (Present (E) and then Suppress_Overflow_Checks (E));
end Overflow_Checks_Suppressed;
-----------------------------
-- Range_Checks_Suppressed --
-----------------------------
function Range_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Range_Checks
or else (Present (E) and then Suppress_Range_Checks (E));
end Range_Checks_Suppressed;
-------------------------------
-- Storage_Checks_Suppressed --
-------------------------------
function Storage_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Storage_Checks
or else (Present (E) and then Suppress_Storage_Checks (E));
end Storage_Checks_Suppressed;
---------------------------
-- Tag_Checks_Suppressed --
---------------------------
function Tag_Checks_Suppressed (E : Entity_Id) return Boolean is
begin
return Scope_Suppress.Tag_Checks
or else (Present (E) and then Suppress_Tag_Checks (E));
end Tag_Checks_Suppressed;
end Checks;