Ada-Rapporteur-Group / User-Community-Input

Ada User Community Input Working Group - Github Mirror Prototype
27 stars 1 forks source link

Name resolution vs. legality for aspects, attributes, and pragmas #45

Closed sttaft closed 11 months ago

sttaft commented 1 year ago

Steve Baird made this comment on the ARG website on 16-May-2023:

Problem Description:

Suppose we have

   type T is ... with Constant_Indexing => Foo;
   function Foo (X : T; Index : Positive) return Boolean; -- suitable
   procedure Foo; -- unsuitable

Is this example legal? The answer depends on whether the rules of 4.1.6(2,3) are name resolution rules (in which case the example is legal) or legality rules (in which case the example is illegal). The RM doesn't say, so some sort of clarification is needed. This is one instance of a more general problem.

Possible Solution:

Unlike other constructs, the RM's definitions of attributes, aspects, and pragmas often fail to explicitly distinguish between name resolution rules and legality rules. This makes the definitions shorter and usually does not introduce any problems. Usually, but not always (see problem description).

As is often the case, there are two parts to the problem: deciding what rule we want to express and then finding the best way to capture that intent in the RM.

Presumably we want at least the rules that apply to the example given in the problem description to be name resolution rules. It seems clear that we do not want to reject something like

  package Pkg1 is
      type Enum is (Aaa, Bbb, Ccc, Foo);
   end Pkg1;

   with Pkg1;
   use Pkg1;
   package Pkg2 is
      type T is ... with Constant_Indexing => Foo;
      function Foo (X : T; Index : Positive) return Boolean;
   end Pkg2;

because the name "Foo" in the aspect specification resolves to the enumeration element (as well as to the suitable function).

But even with that in mind, more than one alternative is available. For example, one could imagine classifying the 4.1.6(2,3) rules that require access-to-constant or access-to-variable parameter types (as opposed to simply requiring an access parameter) as legality rules while classifying the other rules as name resolution rules. This would be consistent with the general principle that constancy participates in name resolution as little as possible. I'm not advocating this, just pointing out that it is an option.

And there is the question of which other aspects have similar issues. Consider, for example, the rules given in 5.5.1(8) in the definition of the Default_Iterator aspect.

Instead of answering this question separately for each aspect that needs it, it might be better to have a general rule stated in only one place (perhaps somewhere in 13.1.1) that rules about what a name-valued aspect (or part of an aspect, as in the case of the Aggregate aspect) shall denote are considered to be name resolution rules unless specified otherwise. This is really only needed in the case where the name can denote an overloadable construct (such as a subprogram); the name-resolution-vs-legality distinction doesn't matter much in the case of aspect such as Iterator_Element that denotes a non-overloadable entity (such as a type).

ARG-Editor commented 1 year ago

When Steve originally raised this question with me, I thought that answer was obvious: these are only Legality Rules. In general, we don't want Name Resolution Rules to be too "smart".

Moreover, we have an ACATS test (B416001), originally created by an ARG member (Gary Dismukes) way back in 2013, which tests that these rules are enforced. The 17th test case in the test program checks that Constant_Indexing and Variable_Indexing aspects are rejected if there are a pair of functions with the appropriate name, one of which has the expected profile and one of which does not. All known Ada 2012 compilers pass this test, so existing implementations are not interpreting these rules as Name Resolution Rules.

This means, in particular, that Steve's first example is illegal; both subprograms match the parameter, are in the correct declaration list, and so on, but one has the wrong profile. This is the same case as tested in the ACATS test.

Steve's second example, of the use clause, gives me pause, however. It would be rather unfriendly if the addition of a use clause somewhere (say, in the context clause of a unit) for use in some other declaration, could make some previously legal aspect illegal. So it seems that something should be a Name Resolution Rule, in order that we exclude at least use-visible subprograms (which can never be legal for these aspects, given the other requirements).

On the other hand, we definitely do not want to make this resolution too "smart". Steve notes that we do not want rules about constancy being resolution rules. I think we also want to avoid "whipsawing" implementers -- we have an ACATS test enforcing a particular interpretation of these rules, which is widely implemented (given that it has been in the ACATS for 9+ years) -- we don't want to be making implementers change their implementation significantly.

On the more general issue, I think all of the aspects that Steve discussed were designed to be Legality Rules rather than Name Resolution Rules. I don't think we want to change that significantly, if at all, and we need to consider each aspect individually to see if there is a possible problem. (Use-visibility could introduce an error in previously legal code only for overloadable entities; otherwise the directly visible entity would hide any use-visible entities. So that is only a problem for aspects that define one or more subprograms.)

One possible way to fix the use-visibility problem without changing much else would be to make the rule that the entity(s) specified for Constant_Indexing/Variable_Indexing be defined in the same declaration list a Name Resolution Rule (leaving all other rules Legality Rules). That would eliminate use-visible (and all other outer subprograms) from consideration, and probably would be the least change that would avoid the maintenance hazard posed by an all-Legality Rule. (If someone is adding subprograms to the actual declaration list, they should not be surprised if something related breaks.)

For Constant_Indexing/Variable_Indexing, making the profile rules Name Resolution Rules would be a new kind of resolution capability for Ada, as the profiles are only partially specified (parameters other than the first can be anything). Resolving a partially specified profile seems to me to be significantly more complicated than "simply" pruning the list of possible matches based on the location of the declaration (but other implementers need to weigh in on this).

In any case, whether we even want to try to fix this issue (the use-clause maintenance hazard) is not completely clear to me. The problem case is fairly unlikely, and the fix is rather heavy on both implementations and in the wording of the Standard.

As such, this seemed like a topic that needs discussion here before progressing it to the ARG agenda.

                 Randy.
sttaft commented 1 year ago

Somehow probably the notion of a "local_name" should fit into the rules for aspect specifications to avoid the "use" visibility problem. Local_name is used for pragmas and rep clauses, and would make sense for many aspect specifications as well. It is defined in 13.1(5/1) as:

In an operational item or representation item, if the local_name is a direct_name, then it shall resolve to denote a declaration (or, in the case of a pragma, one or more declarations) that occurs immediately within the same declarative region as the item. If the local_name has an attribute_designator, then it shall resolve to denote an implementation-defined component (see 13.5.1) or a class-wide type implicitly declared immediately within the same declarative region as the item. A local_name that is a libraryunitname (only permitted in a representation pragma) shall resolve to denote the library_item that immediately precedes (except for other pragmas) the representation pragma.

sttaft commented 1 year ago

Actually on looking at various aspect specifications, my recommendation would be that all rules given directly in the initial description of the aspect should be considered Name Resolution rules, and if there are other rules which should not be Name Resolution rules, they should appear separately in a Legality Rules section. In 5.5.1 we already have an example of such separately specified Legality Rules. This means that we might want to pull out some of the rules from the description of Constant_Indexing and Variable_Indexing if we think they are inappropriate as Name Resolution rules, and list them explicitly as Legality Rules.

ARG-Editor commented 1 year ago

Humm. For Constant_Indexing/Variable_Indexing, the intent was that all of the rules were Legality Rules. We probably should have used Local_Name (or something like it), but that was it for the resolution.

The reason for that is that these aspects are designed to refer to sets of overloaded subprograms. If some of the subprograms of that set (in the correct declaration list) don't have the right profile, that is at least as likely to be because of a mistake as something intentional. And it would be very confusing if a subprogram declared in the right place with the right name, but some subtle mistake, silently did not work as an indexing function. How would someone figure that out? The (failed) indexing is likely to be a long way away from the actual error. As I noted the other day, the ACATS expects this error to be detected.

I think this applies to other aspects that are specified with subprograms with partially specified profiles as well (Aggregate comes to mind).

Aspects that are specified with values (like Size) clearly need the proper expected type, so obviously there are some resolution rules involved. But those Name Resolution Rules are specified in 13.1.1. I'm dubious that we want any additional Name Resolution Rules for aspects (with a few limited exceptions, as with the Local_Name restriction that should be applied to most subprogram-valued aspects).

                               Randy.
sttaft commented 1 year ago

I fear we will need to check this on a case-by-case basis, which should allow us to determine if we need to establish a default of legality vs. name resolution, or be explicit in every case. Luckily, there are not that many that explicitly involve subprogram names. Clearly expressions will use normal name resolution given an expected type.

My own sense is that indexing is a special case, because it is specifying a partial profile. You mentioned Aggregate as specifying a partial profile, and I guess what you meant there was that some of the relevant types come from the subprograms that are named, rather than vice-versa. So that would be a weird sort of "expected profile." But for aspects where we can give a complete "expected profile", I would presume that the rule would be considered a Name Resolution rule. Again, a case-by-case verification seems necessary.

We also have the cases where the subprograms are required to be primitives. I wonder in those cases whether at a minimum we should be considering that rule to be a Name Resolution rule, allowing us to effectively ignore all non-primitives that happen to be visible.

ARG-Editor commented 1 year ago

Ah, you've come around to my position (more or less). I don't think we can just come up with some sort of blanket reasoning for resolution here, particularly given that some items are really sets of related subprograms and some are only partially specified.

Now we just have to find someone that wants to put in the time to deal with this. :-)

           Randy.
ARG-Editor commented 11 months ago

AI22-0084-1 includes this issue.

ARG-Editor commented 11 months ago

The ARG approved AI22-0084-1 at meeting #63A, so this issue has been closed.