Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions features/rfc-spark-values-at-labels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
Feature Name: value_at_labels
Start Date: 2025-10-30
Status: Planning

Summary
=======

This RFC introduces a new attribute, called ``At`` that can be applied to
an expression to refer to a copy of its value saved at a preceding label.
This attribute can be thought as a generalization of the ``Old`` and
``Loop_Entry`` attributes.

Motivation
==========

Complex proof in GNATprove can require writing intermediate assertions to
guide proof, relating values of the program state between multiple steps.
Currently, the only way to state anything about these values is to manually
save the required values in ghost constant declarations. The ``At``
attribute is intended to provide concise syntactic sugar to automate that
process. The user would only need to add a label at the preceding control
flow point, rather than full ghost constant declarations for everything that
need to be saved.


Guide-level explanation
=======================

The ``'At`` attribute takes a label as argument, and can be used on
arbitrary expressions to denote a constant declared at that label,
and initialized with that expression. A program using the ``'At``
attribute is equivalent to a program declaring the constant explicitly
at that label. As an example, the following code:

```ada
X := 1;
<<My_Label>>
X := 2;
pragma Assert ((X-1)'At (My_Label) = 0);
```

is equivalent to

```ada
X := 1;
declare
Compiler_Generated_Unique_Name : constant Integer := (X-1);
begin
X := 2;
pragma Assert (Compiler_Generated_Unique_Name = 0);
end;
```

That equivalence implies that it is not allowed to refer to the value of
a constant at a non-visible (or following) labels. The equivalence also

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "label"

means that the scope of the constant is the surrounding sequence of
statements of the label. In particular, associated finalization will
occurr at the end of the sequence, rather than immediately after the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "occurr" -> "occur" (there are two similar ones below).

reference.


Reference-level explanation
===========================

The attribute ``'At`` can be applied to any subexpression, and takes
a ``statement_identifier`` as parameter. That ``statement_identifier``
shall refer to a visible ``statement_identifier``, denoting a statement
immediately within a sequence of statement enclosing the ``'At`` attribute
reference. (visibility as defined by Ada RM is not strict enough,
as ``statement_identifier`` are implicitly declared in innermost declare block)
Comment on lines +69 to +70
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example to illustrate this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my understanding this means that something like

X := 1;
<<My_Label>>
X := 2;
declare
   Y : Integer := 0;
begin
   pragma Assert ((X-1)'At (My_Label) = Y);
end;

is not allowed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A label is declared in the innermost declare block enclosing the label. So this would be legal.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few lines below: "Furthermore, the statement_identifier referenced by an 'At attribute and the attribute itself shall have the same innermost enclosing body."

To my understanding, this would make it illegal as <<My_Label>> and (X-1)'At (My_Label) do not have the same innermost enclosing body.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enclosing body is not the same as enclosing declare block. A "body" refers to a function, procedure, task, or package body.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example to illustrate this?

Something like

declare
  X : Integer := 0;
begin
  X := 1;
  if Condition then
     <<L>>
     X := 2;
  end if;
  pragma Assert ((X-1)'At (L) = 1);
end;

must not be allowed. However Ada language rules states that the label L is declared in the innermost enclosing declare block, which makes it visible at the assertion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "goto" statement legality rule is similar, and perhaps you should copy as nearly as possible the wording of 5.8(4):

The innermost sequence_of_statements that encloses the target statement shall also enclose the goto_statement. Furthermore, if a goto_statement is enclosed by an accept_statement or a body, then the target statement shall not be outside this enclosing construct.

So, perhaps:

The innermost sequence_of_statements that encloses the target statement shall also enclose the At reference. Furthermore, if an At reference is enclosed by an accept_statement or a body, then the target statement shall not be outside this enclosing construct.

Within that enclosing sequence, the ``'At`` attribute shall occurr within a
statement of the sequence that occurrs after the ``statement_identifier``
it references. Furthermore, the ``statement_identifier`` referenced by
an ``'At`` attribute and the attribute itself shall have the same
innermost enclosing body.

The attribute ``'At`` denotes a constant that is implicitly declared at
the label, following the same rules as local declarations without blocks.
The declaration of the constant is the same as what would be declared for
an unconditionally evaluated ``'Old`` attribute (ARM 26.*/4). In particular,
for tagged types, the constant renames a class-wide temporary in order to
preserve the tag.

The prefix of an ``'At`` attribute reference shall only reference entities
visible at the location of the referenced ``statement_identifier``, or declared
within the prefix itself. It shall not contains a ``'Loop_Entry`` reference
without an explicit loop name. If the prefix of an ``'At`` attribute reference contains
another ``'At`` attribute reference, or a ``'Loop_Entry`` reference (with an explicit
loop name) the inner reference shall be legal at the location of the
``statement_identifier`` referenced by the outer attribute. Similarly, if the
prefix of an ``'Loop_Entry`` attribute reference contains a ``'At`` attribute reference,
the ``'At`` reference shall be legal at the location immediately before the referenced
loop.
(Explanation: the reference should be legal and keep the same meaning when the expression
of the surrounding reference is moved to the implicit declaration point).

The prefix of an ``'At`` attribute reference which is potentially unevaluated
within the outermost enclosing expression shall statically name an entity,
unless the pragma Unevaluated_Use_Of_Old is set to a value that would relax
the matching restriction for attributes ``'Old``/``'Loop_Entry``.