Version 1.3 of ai05s/ai05-0090-1.txt

Unformatted version of ai05s/ai05-0090-1.txt version 1.3
Other versions for file ai05s/ai05-0090-1.txt

!standard 4.1.3(8-9.2/2)          08-02-04 AI05-0090-1/01
!standard 9.1(9.2/3)
!standard 9.4(11.1/3)
!class binding interpretation 08-02-04
!status work item 08-02-04
!status received 08-02-04
!priority Medium
!difficulty Medium
!qualifier Omission
!subject Ambiguities with prefixed views of synchronized primitives
!summary
A selected_component whose selector_name can denote the (implicit) overriding of an inherited primitive of a synchronized type as well as an entry or protected subprogram of that type that implements the inherited primitive is not ambiguous, and resolves to the entry (or protected subprogram).
!question
Consider the following example:
package Synch_Pkg is
type Synch_Interface is synchronized interface;
procedure Prim (S : in out Synch_Interface) is abstract;
task type Task_Type is new Synch_Interface with entry Prim; entry Other_Prim; end Task_Type;
-- Implicit declaration of overriding primitive (as per AI05-0042): -- procedure Prim (S : in out Task_Type);
procedure Other_Prim (Tsk : in out Task_Type);
end Synch_Pkg;
...
T : Synch_Pkg.Task_Type;
...
T.Prim; -- (1) Ambiguous? (No.)
T.Other_Prim; -- (2) Ambiguous? (Yes.)
The call to Prim at (1) using prefixed notation appears to be ambiguous, because the selector could denote either the entry Prim, or the implicitly declared overriding of the primitive inherited from Synch_Interface (which is defined to exist by AI05-0042). This is clearly not what is intended.
Also, there can be subprograms with prefixed views that are homographic with an entry of the task type, such as Other_Prim at (1), but that means that calls using prefixed notation are ambiguous, because the call could be either to the subprogram or to the entry (by 4.1.3 paragraphs 8-9.2/2). Are such calls intended to be ambiguous? (Yes.)
!recommendation
(See summary.)
!wording
[Note: This AI has not been discussed yet, so it's probably premature to draft any wording, but here's a suggestion of how this might be fixed.]
To address the issue of preventing the ambiguity between the implicit overriding declared for an inherited interface operation and an entry or protected subprogram that implements it, one possibility would be to introduce some kind of preference rule, presumably in 4.1.3. Another approach would be to say that one or the other declarations is hidden from all visibility at the point of a selector_name (excluding the expanded name case).
Here's possible wording based on the latter, the intent of which is to make the implicitly declared overriding subprogram hidden in the context of the selector of a selected name that has a prefix of the synchronized type:
In 8.3, add an additional bullet after paragraph 20.1/2:
The declaration of an implicit subprogram that overrides a subprogram inherited by a task or protected type from a synchronized interface and implemented by an entry or protected subprogram (as defined 9.1(9.2/3) and 9.4(11.1/3)) is hidden from all visibility at the point of a selector_name in a selected_component whose prefix resolves to an object or value of the type.
[Or perhaps the preference should really be the other way around, with with the entry or protected subprogram being hidden from all visibility in a prefixed call:
The declaration of an entry or protected subprogram that implements a subprogram inherited from a progenitor type is hidden from all visibility at the point of a selector_name in a selected_component whose prefix resolves to an object or value of the parent task or protected type.
]
!discussion
The ambiguity in the case of the call at (2), to a synchronized operation declared within a task or protected type that is a homograph of the prefixed view of an explicit subprogram seems unavoidable. We could try to define some preference rule similar to that for components that conflict with prefixed views, as is done in 4.1.3(9.2/2), to select the entry or protected subprogram interpretation, but that seems like a bad idea. For one thing, unlike a component, such operations are overloadable. Giving preference to either the synchronized operation or the subprogram with the homographic prefixed view could too easily result in confusion as to which operation was being named. Another possibility would be to try and make certain declarations illegal, but that isn't appealing either, as it would introduce illegalities for no particularly good reason (plus it wouldn't work for technical reasons). So it seems better to leave the language as it is, with the potential for ambiguities, but ones that can generally be avoided either by changing names, using normal function call notation rather than a prefixed view for calls to the implicit overriding subprogram, or by using named notation on calls to distinguish which operation is wanted. Thus, the answer to this part of the question is really just a confirmation.
The first part of the question (concerning the call at (1)) involves a similar case, but one where we definitely want the call to resolve to one of the two available interpretations, since otherwise prefixed notation can't be used at all.
The situation is that of a subprogram inherited from a synchronized interface that is implemented by an entry or protected subprogram. By AI05-0042, such an inherited subprogram is defined to be overridden by an implicit subprogram (see the last sentence of 9.1(9.2/3) and 9.4(11.1/3)). The problem is that a call to the implicitly declared overriding subprogram using prefixed notation will be ambiguous, because it could also be interpreted as a call to the entry or protected subprogram that implements the inherited subprogram. In some cases such a call could be disambiguated using named associations for the parameters, but having to futz with parameter names to disambiguate calls would only be an ugly workaround and an inconvenience for users.
Clearly we need a change to effectively give preference to one of the two operations. The proposed fix is to prefer the interpretation of the entry (or protected subprogram), and to consider the implicit overriding operation effectively hidden at the point of a selector_name in a prefixed call. It seems somewhat more natural to prefer the entry or protected subprogram over the implicit overriding subprogram since the former is explicit in the program text (though formulating the rule the other way could also be considered). In terms of the rules, this is implemented by making the implicit overriding subprogram hidden from all visibility at the point of a selector_name in cases that would otherwise be ambiguous.
--!corrigendum 4.1.3(8-9.2/2)
!ACATS Test
An ACATS C-Test should be written to check that the legal case in the example actually works.
!appendix

From: Gary Dismukes
Sent: Monday, February  4, 2008  4:44 PM

This is the draft version of the AI assigned to me in Fairfax (not assigned
a number yet) [this is version /01 of the AI - ED],
which arose out of a question raised during the meeting.
There are actually two similar questions involving apparent ambiguities
that can occur with prefixed calls to operations of synchronized tagged
types considered here.  One of these cases of ambiguities doesn't seem
to require any action, but the other one needs correction.  The most
appropriate way to fix this can be discussed at the upcoming meeting
(assuming this makes it to the agenda), but in this draft I've suggested
one way that it could be fixed.

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

From: Edmond Schonberg
Sent: Monday, February  4, 2008  5:08 PM

...
>    procedure Prim (S : Synch_Interface) is abstract;

S should be an in-out parameter if it is to be implemented by an entry.

...
> The call to Prim at (1) using prefixed notation appears to be ambiguous,
> because the selector could denote either the entry Prim, or the implicitly
> declared overriding of the primitive inherited from Synch_Interface (which
> is defined to exist by AI05-0042).  This is clearly not what is  
> intended.

Agreed.

> [Or perhaps the preference should really be the other way around, with
> the implicit overriding subprogram being hidden from all visibility in
> a prefixed call:
>
>   The declaration of an entry or protected subprogram that implements
>   a subprogram inherited from a progenitor type is hidden from all
>   visibility at the point of a selector_name in a selected_component
>   whose prefix resolves to an object or value of the parent task or
>   protected type.

But in that case the second declaration would be legal, because the  
implicit subprogram would  then be overridden by the explicit one  
according to the usual rules, and there would be no ambiguity.   So  
the first choice is preferable (if we want (2) to remain ambiguous.

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

From: Gary Dismukes
Sent: Monday, February  4, 2008  6:35 PM

...
> >    procedure Prim (S : Synch_Interface) is abstract;
> 
> S should be an in-out parameter if it is to be implemented by an entry.

You're right, thanks for catching.  I missed going back and making that
correction despite having corrected my compilation test of the same code.
[Correction made in version /01 of the AI - ED.]

> > [Or perhaps the preference should really be the other way around, with
> > the implicit overriding subprogram being hidden from all visibility in
> > a prefixed call:

(Aargh, I just noticed that I failed to adjust the wording on the above
paragraph after swapping some of my text around.  That one should read
"with the entry or protected subprogram being hidden...".  That's what
I get for running out of time and not doing a final careful review of
what I'd written.  At least the next paragraph reflected what I meant
in the above.) [Correction made in version /01 of the AI - ED.]

> >   The declaration of an entry or protected subprogram that implements
> >   a subprogram inherited from a progenitor type is hidden from all
> >   visibility at the point of a selector_name in a selected_component
> >   whose prefix resolves to an object or value of the parent task or
> >   protected type.
>
> But in that case the second declaration would be legal, because the  

(I assume you mean "the second call" above rather than "second declaration".)

> implicit subprogram would  then be overridden by the explicit one  
> according to the usual rules, and there would be no ambiguity.   So  
> the first choice is preferable (if we want (2) to remain ambiguous.

I think there are two confusions here.  First, the operation
Other_Prim called at (2) isn't a case of overriding, as there's
no inherited operation by that name in my example.  But second,
if it were an overriding, then there would be a violation of the
rules in 9.1(9.6/2-9.8/2), which disallow having both an overriding
primitive and an "implemented by" operation for an inherited subprogram.
So I don't think that the fix for (1) would have any effect on the
ambiguity of cases such as (2).

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

From: Edmond Schonberg
Sent: Monday, February  4, 2008  10:27 PM

> I think there are two confusions here.  First, the operation
> Other_Prim called at (2) isn't a case of overriding, as there's
> no inherited operation by that name in my example.  But second,
> if it were an overriding, then there would be a violation of the
> rules in 9.1(9.6/2-9.8/2), which disallow having both an overriding
> primitive and an "implemented by" operation for an inherited  
> subprogram.
> So I don't think that the fix for (1) would have any effect on the
> ambiguity of cases such as (2).

Right, I misread the example, Other_Prim is well-named, and this is  
definitely (unambiguously?) ambiguous.

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

From: Tucker Taft
Sent: Monday, February  4, 2008  8:21 PM

> ... Another possibility would be
> to try and make certain declarations illegal, but that isn't appealing
> either, as it would introduce illegalities for no particularly good
> reason (plus it wouldn't work for technical reasons).

Can you explain these "technical reasons"?
We make it illegal to have an entry or
protected subprogram that is homographic with the
prefixed view of an explicit overriding.  Why can't we
do the same for an explicit non-overriding primitive?

> ...
> Clearly we need a change to effectively give preference to one of the
> two operations.  The proposed fix is to prefer the interpretation of
> the entry (or protected subprogram), and to consider the implicit
> overriding operation effectively hidden at the point of a selector_name
> in a prefixed call.  It seems somewhat more natural to prefer the entry
> or protected subprogram over the implicit overriding subprogram since
> the former is explicit in the program text (though formulating the
> rule the other way could also be considered).

I strongly prefer having the entry/protected subprogram
visible via prefixed notation.  That is what you would
get in the absence of the interface, and it seems weird
that the formal parameter names, defaults, etc., would
suddenly change just because the task type now implements
an interface.  So I agree with your current solution, and
would be opposed to the alternate you considered.

> ...In terms of the rules,
> this is implemented by making the implicit overriding subprogram
> hidden from all visibility at the point of a selector_name in cases
> that would otherwise be ambiguous.

Do we really need to use the term "hidden from all visibility" here?
I think all you need to do is have 4.1.3(9.2) include a rule
that disallows referring to a primitive subprogram that is implemented
by an entry or protected subprogram.  Remember that 4.1.3(9.2)
is all considered "Name Resolution" so disallowing something is
effectively creating an overload resolution rule.  Hiding from
"all visibility" seems like overkill, and is probably exactly
what we *don't* want to do, since we still want to be able to
refer to the implicitly declared primitive using "unprefixed"
subprogram call notation.

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

From: Gary Dismukes
Sent: Wednesday, February  6, 2008  12:11 AM

> > ... Another possibility would be
> > to try and make certain declarations illegal, but that isn't appealing
> > either, as it would introduce illegalities for no particularly good
> > reason (plus it wouldn't work for technical reasons).
> 
> Can you explain these "technical reasons"?
> We make it illegal to have an entry or
> protected subprogram that is homographic with the
> prefixed view of an explicit overriding.  Why can't we
> do the same for an explicit non-overriding primitive?

You're right, we could do that, and I started off by doing that.
But there are other cases that that have a similar effect where
the conflicting operation isn't a primitive, such as a class-wide
subprogram:

  procedure Other_Op (T : Synchronized_Type'Class);

Do we want to disallow that as well?  I'm assuming we don't.

At the time it had seemed to me to be needlessly restrictive to
disallow any of these cases.  After all, an implementation can
always issue a warning that certain calls will be ambiguous.  I was
thinking that a user might have some reason to declare an explicit
primitive that conflicts, and have a homographic task entry that
can still be called from within the task even though it can't be
called from outside.  But I'm willing to admit that that's a stretch,
and probably isn't an idiom worth allowing, so if the feeling is that
such a conflict with a primitive almost certainly indicates an error,
then I'm willing to go along with making the case of a primitive
illegal.

> > ...
> > Clearly we need a change to effectively give preference to one of the
> > two operations.  The proposed fix is to prefer the interpretation of
> > the entry (or protected subprogram), and to consider the implicit
> > overriding operation effectively hidden at the point of a selector_name
> > in a prefixed call.  It seems somewhat more natural to prefer the entry
> > or protected subprogram over the implicit overriding subprogram since
> > the former is explicit in the program text (though formulating the
> > rule the other way could also be considered).
>
> I strongly prefer having the entry/protected subprogram
> visible via prefixed notation.  That is what you would
> get in the absence of the interface, and it seems weird
> that the formal parameter names, defaults, etc., would
> suddenly change just because the task type now implements
> an interface.  So I agree with your current solution, and
> would be opposed to the alternate you considered.

For some reason I was wavering, then ended up leaning towards
the recommended preference, but hadn't fully solidified my commitment
to that by the time I wrote it up, even though it felt right to me
based on wanting to get the formal names, etc.  However, your comment
has convinced me that it's the right choice. :-)  (I was a little
worried that some semantic glitch might pop up that would argue for
preferring the other, so wanted to leave the option open, but that was
most probably just FUD.)

> > ...In terms of the rules,
> > this is implemented by making the implicit overriding subprogram
> > hidden from all visibility at the point of a selector_name in cases
> > that would otherwise be ambiguous.
> 
> Do we really need to use the term "hidden from all visibility" here?
> I think all you need to do is have 4.1.3(9.2) include a rule
> that disallows referring to a primitive subprogram that is implemented
> by an entry or protected subprogram.  Remember that 4.1.3(9.2)
> is all considered "Name Resolution" so disallowing something is
> effectively creating an overload resolution rule.  Hiding from
> "all visibility" seems like overkill, and is probably exactly
> what we *don't* want to do, since we still want to be able to
> refer to the implicitly declared primitive using "unprefixed"
> subprogram call notation.

I defined it as hidden only within the specific context of the
selector_name, not hidden in general, so that it wouldn't affect
normal calls, but I admit that's an unorthodox use of "hidden from
all visibility".  Anyway, I'm fine with going the route of adding
a rule in 4.1.3 to cover this case, and will try to draft some
wording for that.

BTW, in your comment above you say "disallows referring to a primitive
subprogram that is implemented by...", but is that really what we
want?  That would disallow dispatching calls using prefixed notation.
My attempted fix was written to hide the implicit primitive only for
calls that could be made to the entry itself, that is, calls where
the prefix is of the specific type, but to still allow calls with
class-wide prefixes to be made to the dispatching primitive.

It seems unfortunate to lose the dispatching calls, though obviously
those would still be possible using normal call notation, just not with
prefixed notation.  Wouldn't it be preferable to try and formulate the
rule so as to also permit dispatching calls that use prefixed notation?

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

From: Tucker Taft
Sent: Wednesday, February  6, 2008  5:30 AM

> You're right, we could do that, and I started off by doing that.
> But there are other cases that that have a similar effect where
> the conflicting operation isn't a primitive, such as a class-wide
> subprogram:
> 
>   procedure Other_Op (T : Synchronized_Type'Class);
> 
> Do we want to disallow that as well?  I'm assuming we don't.

These aren't homographs, so trying to make these illegal
is probably more trouble than it is worth.  But that doesn't
change my mind about disallowing the more common case of
a homographic primitive.
> 
> At the time it had seemed to me to be needlessly restrictive to
> disallow any of these cases.  After all, an implementation can
> always issue a warning that certain calls will be ambiguous.  I was
> thinking that a user might have some reason to declare an explicit
> primitive that conflicts, and have a homographic task entry that
> can still be called from within the task even though it can't be
> called from outside.  But I'm willing to admit that that's a stretch,
> and probably isn't an idiom worth allowing, so if the feeling is that
> such a conflict with a primitive almost certainly indicates an error,
> then I'm willing to go along with making the case of a primitive
> illegal.

I think that is more consistent.  It seems weird that it is illegal
to have a homographic overriding, but it is OK to have a homographic
operation that does *not* override an inherited primitive.  I don't
think you are doing the user any favors by having this distinction.

...
> It seems unfortunate to lose the dispatching calls, though obviously
> those would still be possible using normal call notation, just not with
> prefixed notation.  Wouldn't it be preferable to try and formulate the
> rule so as to also permit dispatching calls that use prefixed notation?

I'm not sure what you mean by "losing" dispatching calls.
If I have a prefix of task_type'Class, I still have visibility
on the entries of task_type I hope!  I just don't need to
do any "dispatching" to know which entry, since you can't
further extend a concrete task type.  This is similar to referring
to the components of a record type when the prefix of the
selected component is record_type'Class.  No dispatching is
involved.

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

Questions? Ask the ACAA Technical Agent