AI22-0079-1
!standard 8.6(17/3) 23-06-28 AI22-0079-1/03
!class Binding Interpretation 23-06-05
!status Amendment 1-2022 23-06-27
!status WG9 Approved 23-10-12
!status ARG Approved 7-0-0 23-06-11
!status work item 23-06-05
!status received 23-06-05
!submitter Randy Brukardt
!priority Low
!difficulty Easy
!qualifier Omission
!subject Parameters of a protected type
Parameters of callable operations of task and protected types can refer to that type; it does not represent the current instance of the type in that context.
Is the following example legal?
protected type PMap is
function Equal (Right : PMap) return Boolean; -- ??
-- Left operand is implicit.
function Indir_Equal (Right : access PMap) return
Boolean; -- OK.
-- Left operand is implicit.
...
private
...
end PMap;
Not by current rules: 8.6(17/3) says that PMap is the current instance of type PMap whenever you are inside of PMap (with some exceptions we'll get to in a moment). The current instance of a type is an object. And of course it is not legal to name an object as the subtype of a parameter.
We have some exceptions in 8.6(17/3) so that obvious, unambiguous cases mean what a programmer would expect them to mean. These are all places where only a (sub)type name is allowed. In particular, we treat the designated type of an access-to-object type, and the parameters and result types of access-to-subprogram types as the type itself, not as the current instance. That is why the declaration for Indir_Equal is legal.
It seems reasonable to have an additional exception for the parameter and result types of callable entities declared within the type declaration. (This would include both protected operations and task entries.) These also are places that can only name a (sub)type; a current instance (which is an object) cannot work there. We certainly do not want unambiguous, natural code to be illegal.
Note that examples like this would come up if, for instance, you wanted to create a map container using a protected object. An equality operation would normally be expected, and it necessarily would need to operate on two instances of the type. Other operations like Move and Copy also would need to operate on more than one instance of the type.
(See summary.)
Modify 8.6(17/3):
If a usage name appears within the declarative region of a type_declaration and denotes that same type_declaration, then it denotes the current instance of the type (rather than the type itself); the current instance of a type is the object or value of the type that is associated with the execution that evaluates the usage name. Similarly, if a usage name appears within the declarative region of a subtype_declaration and denotes that same subtype_declaration, then it denotes the current instance of the subtype. These rules do not apply if the usage name appears within{:
It is possible to do this with with the current rules, but a partial view and subtype is required:
package Pack1 is
type PMap1 is limited private;
...
private
subtype Local_PMap1 is PMap1;
protected type PMap1 is
function Equal (Right : Local_PMap1) return
Boolean; -- OK.
-- Left operand is implicit.
...
private
...
end PMap1;
end Pack1;
But the principle of least surprise suggests that we should allow using PMap1 directly and not require this level of hoop-jumping.
During the discussion of this AI, it was pointed out that sometimes (especially for task types) it can make sense to declare another object of the type. We therefore added object_declarations to the exceptions.
(See issue.)
@drepl
If a usage name appears within the declarative region of a @fa{type_declaration} and denotes that same @fa{type_declaration}, then it denotes the @i{current instance} of the type (rather than the type itself); the current instance of a type is the object or value of the type that is associated with the execution that evaluates the usage name. Similarly, if a usage name appears within the declarative region of a @fa{subtype_declaration} and denotes that same @fa{subtype_declaration}, then it denotes the current instance of the subtype. These rules do not apply if the usage name appears within the @fa{subtype_mark} of an @fa{access_definition} for an access-to-object type, or within the subtype of a parameter or result of an access-to-subprogram type.
@dby
If a usage name appears within the declarative region of a @fa{type_declaration} and denotes that same @fa{type_declaration}, then it denotes the @i{current instance} of the type (rather than the type itself); the current instance of a type is the object or value of the type that is associated with the execution that evaluates the usage name. Similarly, if a usage name appears within the declarative region of a @fa{subtype_declaration} and denotes that same @fa{subtype_declaration}, then it denotes the current instance of the subtype. These rules do not apply if the usage name appears within:
@xbullet{the @fa{subtype_mark} of an @fa{access_definition} for an access-to-object type;}
@xbullet{the @fa{subtype_mark} of a @fa{subtype_indication} of an @fa{object_declaration};}
@xbullet{the @fa{subtype_mark} of an @fa{object_renaming_declaration}; or}
@xbullet{the subtype of a parameter or result of a callable entity or an access-to-subprogram type.}
An ACATS C-Test should try an example similar to the one in the !issue. We should check if any existing ACATS tests require cases like this to be illegal.
From: Randy Brukardt
Sent: Tuesday, Jun 27, 2023 at 6:47 PM
In finishing up this (approved) AI, I happened to notice that there is another case that probably should be added to the list.
Recall that during the meeting, Jean-Pierre Rosen asked to add object_declarations to the list of exceptions to the current instance rules. This made good sense (there is no possible ambiguity, and there is no important reason to require jumping through hoops).
However, it strikes me that the case of object renames is very similar to object_declarations, but the rule as written does not make an exception for them. (There are no type renames, so there is no ambiguity with renames.)
This doesn't make a ton of sense:
task type Tsk is
entry Initial (Data : in Natural);
end Tsk
task body Tsk is
-- Tsk is a current instance (object) in here, except for the
-- old and new exceptions to the current instance rule.
My_Data : Natural;
begin
accept Initial (Data : in Natural) do
My_Data := Data;
end Initial;
if Func(My_Data) then
declare
New_Tsk : Tsk; -- Allowed by the rules updated by AI22-0079-1.
Alt_Tsk : Tsk renames New_Tsk; -- Illegal, Tsk is an object.
Alt2_Tsk : renames New_Tsk; -- OK.
begin
...
end;
else
...
end if;
end Tsk;
I suggest adding an additional bullet to the list of exceptions in 8.6:
the subtype_mark of an object_renaming_declaration;
I'll do this as an editorial review comment unless someone objects. I believe that this is an obvious oversight (the intent was that object declarations and renames work as similarly as possible, anything making them more different than necessary is a bug).
If someone does object, the AI will be reopened and discussed at our next meeting.
P.S. The only other similar cases that I could find was the optional subtype_marks in iterators. It doesn't seem very likely to iterate over a task or protected object inside of itself; and a task or PT would never be a cursor type. Other uses of subtype_mark (including in subtype_indications) are ambiguous (like memberships); are parts of expressions (qualified_expressions) and thus would be confusing; or are useless (like a use type clause, since we're only talking about inside the type declaration). So I don't think there are any additional cases that need addressing.
From: Tucker Taft
Sent: Wednesday, June 28, 2023 8:14 AM
Your suggestion makes sense. I also suggested where to add some curly braces to the Google Doc, so please consider those part of my editorial review.