AI22-0045-1

!standard 2.8(6/5)                                    23-02-08  AI22-0045-1/04

!standard 2.8(7/3)

!standard 2.8(7.1/3)

!standard 2.8(7.2/3)

!standard 2.8(12)

!class binding interpretation 22-06-16

!status Corrigendum 1-2022  22-09-09

!status ARG Approved  10-0-0  22-09-09

!status work item 22-06-16

!status received 22-03-22

!priority Medium

!difficulty Medium

!qualifier Clarification

!subject Issues with pragma placement

!summary

Five kinds of pragmas are identified, with their own placement restrictions. Pragma Assert and pragma Inspection_Point are executable pragmas. The other kinds are configuration, context, aspect-related, and control.

!issue

Pragma placement is currently defined in RM 2.8 (5-8) as follows:

Pragmas are only allowed at the following places in a program:

6/5        After a semicolon delimiter, but not within a formal_part, discriminant_part, or declare_expression.

7/3        At any place where the syntax rules allow a construct defined by a syntactic category whose name ends with “declaration”, “item”, “statement”, “clause”, or “alternative”, or one of the syntactic categories variant or exception_handler; but not in place of such a construct if the construct is required, or is part of a list that is required to have at least one such construct.

7.1/3        In place of a statement in a sequence_of_statements.

7.2/3        At any place where a compilation_unit is allowed.

8   Additional syntax rules and placement restrictions exist for specific pragmas.

These rules overlap with one another, and do not make sense for certain kinds of pragmas. Pragmas that are "lexical" in nature, such as turning on and off compiler modes of various sorts, such as listing or optimization, can be accommodated almost anywhere. On the other hand, pragmas that have some executable semantics only make sense in places where execution is normally occurring, such as in a sequence of statements or declarative_items. Should we distinguish such kinds of pragmas here, or rely on additional rules scattered throughout the manual? (define most of the basic rules here)

Other more specific issues:

(1) One would expect that a pragma Assert is a master. We would not want function results from the evaluation of an Assert expression hanging around. AI22-0013-2 attempted to clarify this by modifying 2.8(12) to say:

Any pragma that appears at the place of an executable construct is executed, and is treated for the purposes of other rules of the language as being of the same sort of executable construct. Unless otherwise specified for a particular pragma, this execution consists of the evaluation of each evaluable pragma argument in an arbitrary order.

In the case of a pragma replacing a statement as allowed by 2.8(7.1/2), it is clear that the pragma is treated as a statement. Unfortunately, a statement is not a master construct (that requires a simple_statement), so it appears that a pragma given at the place of a statement is not a master (or at a minimum it is not clear). Since the point of AI22-0013-2 was to clarify questions like this, further clarification is needed.

(2) However, most of the rules of 2.8 are not as clear about the construct that the pragma is at the place of. In particular, 2.8(6/5) ["after a semicolon delimiter"] does not talk about constructs at all. Moreover, multiple placement rules could apply: for example, a pragma following a statement in a sequence_of_statements is allowed by both 2.8(7.1/3) (which is clear about the construct "at the place of") and 2.8(6/5) (which is not). How do we decide which rule is the reason the pragma is allowed at a particular place?

(3) Some of the places that pragmas are allowed are not at the place of any executable construct. What happens for executable pragmas in such locations? When are they executed? Are they a master? And so on.

For example, 2.8(7/3) allows a pragma at the place of an _alternative; this allows a pragma at the head of a case statement:

        

case P is
   pragma Assert (...)  -- (1)
   when 1 => ...           -- (2)
           pragma Assert (...) -- (3)
   when others => ...
end case;

 

It is clear that (3) is at the place of a statement, and thus is executed after the statements at (2) (and only if the statements at (2) are executed). But (1) is not at the place of a statement (it is in place of a case_statement_alternative), and this part of a case statement is never executed.

(4) AI12-0236-1 modified 2.8(6/5) to disallow pragmas in declare_expressions. However, a declare_expression contains a list of declare_items. Therefore, 2.8(7/3) allows pragmas in the list of declare_items, specifically the place that 2.8(6/5) is trying to disallow. Something is wrong here.

!recommendation

In general, we propose to provide more guidelines about pragmas in 2.8.

For (1), we explicitly state the kinds of constructs that are being executed.

For (2), we propose to identify multiple kinds of pragmas, and limit which placement rules apply to each kind of pragma.

For (3), we propose to introduce the notion of an executable pragma as one of the kinds of pragmas (as mentioned in (2) above), and then indicate that the Assert pragma is an executable pragma as is an Inspection_Point pragma.

For (4), we permit executable pragmas within declare expressions (pragma Assert and pragma Inspection_Point are the only language-defined executable pragmas).

!wording

Modify[a] 2.8(5-8) as follows:

{There are five kinds of pragmas: configuration pragmas (and pragmas usable as configuration pragmas), context pragmas, aspect-related pragmas, executable pragmas, and control pragmas, and each has its own placement restrictions:

Additional syntax rules and placement restrictions exist for specific pragmas.

Modify 2.8(12): (Note: This includes the approved modification of AI22-0013-2, which is not yet in any published draft).

An[y] {executable or aspect-related} pragma that appears [at the]{in} place of an executable construct is executed{, and is treated for the purposes of other rules of the language as being a simple_statement, basic_declarative_item, or declare_item according to where it appears}. Unless otherwise specified for a particular pragma, this execution consists of the evaluation of each evaluable pragma argument in an arbitrary order.

Modify 2.8(20):

The forms of {the} List, Page, and Optimize {control} pragmas are as follows:

Modify 4.5.9(7/5):

The following are not allowed within a declare_expression: a declaration containing the reserved word aliased; the attribute_designator Access or Unchecked_Access; an anonymous access type{; or a pragma other than an executable pragma (see 2.8)}.

Modify 10.2.1(23):

A pragma Elaborate or Elaborate_All {is a context pragma (see 2.8)[Redundant:, and} is only allowed within a context_clause{]}.

Modify 11.4.2(4/2):

A pragma Assert {is an executable pragma (see 2.8)[Redundant:, and} is allowed at the place where a [declarative_item]{basic_declaration, declare_item,} or [a] statement is allowed{]}.

Modify 13.1(4/1):

A representation pragma {is an aspect-related pragma (see 2.8)[Redundant:, and} is allowed only at places where an aspect_clause or compilation_unit is allowed{]}.

Modify H.3.2(4):

A pragma Inspection_Point{is an executable pragma (see 2.8)[Redundant:, and} is allowed wherever a [declarative_item]{basic_declaration, declare_item,} or statement is allowed{]}. Each object_name shall statically denote the declaration of an object.

Modify J.15(1/3):

Pragmas can be used as an alternative to aspect_specifications to specify certain aspects. {Such pragmas are aspect-related pragmas (see 2.8).}

!discussion

For (1), we now limit "executable" pragmas to being in place of a simple_statement, a basic_declaration, or a declare_item. As mentioned the only language-defined executable pragmas are Assert and Inspection_Point, and these will be treated as the kind of construct they replace, so you could, for example, put a statement label on an Inspection_Point.

For (2), we define five kinds of pragmas. The term "control" pragma is not ideal, but since it included the List pragma, and the Optimize pragma, it seemed reasonable. "Lexical" pragma seemed like a misnomer for an Optimize pragma.

A more general fix would be to replace 2.8(6/5, 7/3, 7.1/3, and 7.2/3) with actual syntax placement rules. This idea was rejected in AI05-0163-1 because it was "requiring the inspection of more than 100 BNF rules". Tucker Taft suggests that modification of 5 rules would allow all of the places people really care about.

        simple_statement ::= ... | pragma

        library_unit_declaration ::= ... | pragma

        context_item ::=  ... | pragma

        aspect_clause ::=  ... | pragma

        generic_formal_part ::= generic

                        {generic_formal_parameter_declaration | use_clause | pragma}

           

The use of aspect_clause cleverly allows pragmas in virtually all sorts of declarations.

However, these rules would not allow pragmas in a few places the current rules allow, including between the "is" and first "when" of a case statement, and between the "select" and an alternative for select_statements. These positions are only likely to be useful for control pragmas like pragma Page and pragma List(Off), and even then it seems a bit bizarre to split a statement in a listing. However, now that we have multiple kinds of pragmas, using syntax rules gets more complicated, so we have decided to stick with English-based syntax rules.

For (3), we now limit executable pragmas to appearing in place of a simple_statement, a basic_declaration, or a declare_item, so that eliminates the danger of having masters appearing in weird places.

For pragma Assert, there already is a rule (11.4.2(4/2)) which prevents pragma placements like the first one in the third Issue. There are not any other language-defined pragmas that need to execute, though Inspection_Point is almost like an executable construct, since it represents a "break" in the flow of code to allow inspection of an intermediate state. In any case, it is unusual to have two closely related rules in very different parts of the RM, especially as it would be easy for new pragmas to forget the required placement rule. So we have moved the special placement rules for pragma Assert and Inspection_Point to 2.8, but we leave behind redundant versions of them.

For (4), we choose to allow executable pragmas in declare_expressions, but do not allow other kinds of pragmas.

The intent of all of the rules surrounding declare expressions was to keep the implementation extremely simple, and to allow an entire expression to be the smallest unit of change in a compiler. Changing properties in the middle of an expression could be problematic for some implementations, and it did not seem important enough of a capability to require implementations to change their basic design.

For example, how important is it to allow the specification of pragma Suppress and (especially, since it cannot be ignored) pragma Unsuppress in the middle of an expression? Consider this:

procedure P is
    pragma Suppress (All_Checks);
begin
    A := B + (declare
                 pragma Unsuppress (All_Checks);
              begin
                 C * D);
end P;

 

If the implementation normally suppresses overflow checks in the scope of a pragma Suppress, the pragma Unsuppress would have to turn the checks back on - an overflow check is required at the multiply in the above. However, if the implementation uses global flags to control overflow and expressions are evaluated at one time, it might require substantial efforts to get this example to work. (Similar issues apply to other pragmas that affect global settings, such as Optimize.)

These sorts of expressions were designed to be used in assertions like predicates and preconditions. Those are not even evaluated where they are declared, so one would have to be able to defer the effect of a pragma until some later point. For some pragmas, that would be a new capability.

For all of these reasons, we only allow executable pragmas (whose effects are well-defined) in declare_expressions. We considered allowing implementations to support other pragmas, but that would add another potential portability gotcha for little actual capability, so we do not allow other pragmas at all.[b][c]

!examples

(See Issue and Discussion.)

!ACATS test

One could add some B-Tests that the various kinds of pragmas aren’t allowed in places that are allowed for control pragmas, but most of those restrictions already exist for specific pragmas (and should be tested for them), so additional more general tests would not add much.

Similarly, C-tests that executable pragmas are executed at the proper place and acts as a master ought to exist for the specific pragmas, so again more general tests would also not add much.

!appendix

This AI is partially in response to ARG Github Issues #7 (https://github.com/Ada-Rapporteur-Group/User-Community-Input/issues/7) and #9 (https://github.com/Ada-Rapporteur-Group/User-Community-Input/issues/9).

*********************************************************

[a]I've removed the grey highlighting that the original author had. The HTML version of this document automatically highlights insertions and deletions in dark green and grayish red, respectively, and doing it manually conflicts with that. The automatic highlighting sometimes shows errors in insert/delete markings (including in this AI, the first insertion closing was missing!). Moreover, the manually applied gray deletions was in 5 different colors (3 grays, a green, and an orange), adding lots of unnecessary markup. As an approved AI, the need to read this editable version is much lower, so I removed all of the manual formatting.

[b]I updated the discussion to reflect the new rule that only allows executable pragmas.

[c]We forgot to do that at the meeting when this was approved.