[PATCH] ada: Add limitations on the acceptable prefixes of 'Old
Samuel Tardieu
sam@rfc1149.net
Sat Apr 12 01:24:00 GMT 2008
The current implementation of the 'Old attribute has several problems:
1- Using 'Old on a local variable will cause a copy to be made at the
beginning of the subprogram declarative region, that is *before*
the declaration and initialization of the variable being captured. The
following example will crash GNAT:
procedure C is
X : Integer := 0;
begin
X := X'Old;
end C;
2- Taking 'Old on a out parameter is not considered an error, only a
warning, saying that the value is read before being initialized.
3- It is possible to take 'Old on a function, introducing the risk
of having side effects. Moreover, it means that if F is a function,
"F'Old = F'Old" is not guaranteed to return True as F will be called
twice. In fact, it is possible to have any expression before 'Old.
As 'Old is likely to be used in pragma Assert or pre/post conditions,
this may increase the risk of side-effects depending on the current
compilation mode (with or without assertions and checks).
4- It is possible to have 'Old present in a formal default expression.
This exposes some strange behaviour:
Y : Integer := 0;
function F (X : Integer := Y'Old) return Integer is
begin
return X;
end F;
function G return Integer is
begin
Y := 3;
return F;
end G;
Calling G will return 0, while one may expect it to return 3.
Y'Old is captured on entry in the subprogram where the call to
F takes place, that is when entering G.
Also, declaring
Z : constant Integer := F;
is impossible at the top-level as there is no subprogram to capture
the value of Y into. The error message saying that 'Old can only be
used in a subprogram is unclear here.
This patch, which is expected to be applied after "[PATCH v3] ada: Allow
'Old attribute to be deeper in a subprogram" (which expands the capabilities
of 'Old), introduces the following constraints:
- The prefix of 'Old must be an object, to avoid the risk of having side
effects. Takes care of (3).
- The prefix of 'Old cannot appear in the default expression of a parameter
specification. Takes care of (4).
- The prefix of 'Old cannot be a local variable of the current subprogram.
Takes care of (1).
- The prefix of 'Old cannot be an out mode parameter. Takes care of (2).
As those new constraints are related and lead to a few lines of localized
code in Sem_Attr, I send them at once. The test program will show the
following errors:
3. function F (X : Integer := Y'Old) return Integer is
|
>>> attribute "Old" cannot appear in a default expression
8. Y := Y'Old;
|
>>> attribute "Old" cannot refer to local variable
14. IL := IL'Old;
|
>>> attribute "Old" cannot refer to local variable
18. Y := Z'Old;
|
>>> attribute "Old" cannot refer to local variable
21. Y := O'Old;
|
>>> attribute "Old" cannot refer to "out" mode parameter
23. Y := F'Old;
|
>>> prefix of attribute "Old" must be an object
Regtested on i686-pc-linux-gnu.
Ok for trunk?
gcc/ada/
* sem_attr.adb (Analyze_Attribute, Attribute_Old case): Add
restrictions to the prefix of 'Old.
gcc/testsuite/
* gnat.dg/old_errors.adb: New.
---
gcc/ada/sem_attr.adb | 31 +++++++++++++++++++++++++++++++
gcc/testsuite/gnat.dg/old_errors.adb | 24 ++++++++++++++++++++++++
2 files changed, 55 insertions(+), 0 deletions(-)
create mode 100644 gcc/testsuite/gnat.dg/old_errors.adb
diff --git a/gcc/ada/sem_attr.adb b/gcc/ada/sem_attr.adb
index 19d636b..e5fe518 100644
--- a/gcc/ada/sem_attr.adb
+++ b/gcc/ada/sem_attr.adb
@@ -3480,6 +3480,37 @@ package body Sem_Attr is
Error_Attr ("attribute % cannot apply to limited objects", P);
end if;
+ if not Is_Entity_Name (P)
+ or else Ekind (Entity (P)) not in Object_Kind
+ then
+ Error_Attr ("prefix of attribute % must be an object", N);
+ end if;
+
+ declare
+ Id : constant Entity_Id := Entity (P);
+ PN : Node_Id;
+ begin
+ if Ekind (Id) = E_Out_Parameter then
+ Error_Attr
+ ("attribute % cannot refer to OUT mode parameter", N);
+ end if;
+
+ if not Is_Formal (Id)
+ and then Current_Subprogram = Enclosing_Subprogram (Id)
+ then
+ Error_Attr ("attribute % cannot refer to local variable", N);
+ end if;
+
+ PN := Parent (P);
+ while Present (PN) loop
+ if Nkind (PN) = N_Parameter_Specification then
+ Error_Attr
+ ("attribute % cannot appear in a default expression", N);
+ end if;
+ PN := Parent (PN);
+ end loop;
+ end;
+
------------
-- Output --
------------
diff --git a/gcc/testsuite/gnat.dg/old_errors.adb b/gcc/testsuite/gnat.dg/old_errors.adb
new file mode 100644
index 0000000..f8933da
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/old_errors.adb
@@ -0,0 +1,24 @@
+procedure Old_Errors (I : in Integer; O : out Integer; IO : in out Integer) is
+ Y : Integer := 0;
+ function F (X : Integer := Y'Old) return Integer is -- { dg-error "default expression" }
+ begin
+ return 0;
+ end F;
+begin
+ Y := Y'Old; -- { dg-error "cannot refer to local variable" }
+ declare
+ Z : Integer := 0;
+ procedure Inner is
+ IL : Integer := 0;
+ begin
+ IL := IL'Old; -- { dg-error "cannot refer to local variable" }
+ Z := Z'Old;
+ end Inner;
+ begin
+ Y := Z'Old; -- { dg-error "cannot refer to local variable" }
+ end;
+ Y := I'Old;
+ Y := O'Old; -- { dg-error "mode parameter" }
+ Y := IO'Old;
+ Y := F'Old; -- { dg-error "must be an object" }
+end Old_Errors;
--
1.5.5.144.g3e42
More information about the Gcc-patches
mailing list