Version 1.4 of ai05s/ai05-0033-1.txt
!standard 13.11.2(16) 08-04-21 AI05-0033-1/03
!standard C.3.1(7/2)
!standard C.3.1(8/2)
!class binding interpretation 06-12-15
!status ARG Approved 9-0-0 06-11-09
!status work item 06-12-15
!status received 06-10-25
!priority Low
!difficulty Medium
!qualifier Omission
!subject Rules for non-library level interrupt handlers
!summary
(1) pragmas Interrupt_Handler and Attach_Handler are illegal in a protected type
that is declared in a generic body. The library-level requirement is rechecked
in instances, including private parts.
(2) It is erroneous to dereference an access-to-protected-subprogram
if the associated protected object no longer exists.
!question
(1) The last sentence of C.3.1(8/2) reads:
The corresponding protected_type_declaration or
single_protected_declaration shall be a library-level declaration.
This rule seems to be a contract model violation. Consider:
generic
package G is
pragma Elaborate_Body;
end G;
package body G is
protected type Handler is
procedure My_Handler;
pragma Interrupt_Handler (My_Handler);
end Handler;
...
end G;
If G is a library unit, then apparently type Handler is legal, but what
happens if G is instantiated in a nested scope? If we really need the
last sentence of C.3.1(8/2), then we would also need an assume-the-worst
rule in generic bodies, and presumably we would need rechecking in the
private part of instances.
(2) AI95-303 allows objects with interrupt handlers to be declared
anywhere (not just library-level). What does Interrupts.Current_Handler
return in this case? It seems like it would be an access-to-subprogram
value that is not a legitimate value of the library-level
Parameterless_Handler type, and represents a dangling
reference possibility. Do we need some rules to handle this case?
!recommendation
(See Summary.)
!wording
(1) Merge C.3.1(7/2) and C.3.1(8/2) into the following paragraph:
The Attach_Handler and Interrupt_Handler pragmas are only allowed immediately
within the protected_definition where the corresponding subprogram is declared.
The corresponding protected_type_declaration or single_protected_declaration
shall be a library-level declaration, and shall not be declared within a
generic body. In addition to the places where Legality Rules normally apply
(see 12.3), these rules also apply in the private part of an instance of a
generic unit.
Existing AARM Discussion: In the case of a protected_type_declaration, an
object_declaration of an object of that type need not be at library level.
New AARM Discussion: We cannot allow these pragmas in a generic body, because
legality rules are not checked for instance bodies, and these should not be
allowed if the instance is not at the library level. The protected types can
be declared in the private part if this is desired. Note that while the 'Access to
use the handler would provide the check in the case of Interrupt_Handler, there
is no other check for Attach_Handler. Since these pragmas are so similar, we
want the rules to be the same.
[The alternative to this merging is to duplicate the rules; but the whole point
is to make them identical. Might as well make them completely identical. - RLB]
(2)
Replace the first sentence of 13.11.2(16) by:
Evaluating a name that denotes a nonexistent object or a protected subprogram whose
associated object is nonexistent is erroneous.
!discussion
(1)
We want to restrict interrupt handlers to library-level only, so that we
don't have to deal with static links (or displays) in handlers. Therefore,
there needs to be a check somewhere.
For pragma Interrupt_Handler, there is a later use of 'Access which will
provide an accessibility check. So there would not need to be any
restrictions at all on the type (or object, which was changed in AI95-00303).
The check on types provides an early warning that a type has been declared
which can never be used, but this is not critical to have.
On the other hand, for pragma Attach_Handler, there is no 'Access providing
a check.
It's best to have the same rules for Interrupt_Handler and Attach_Handler,
as it is not unusual to switch between the two.
In the example in the question, legality rules are not checked in generic
bodies of instances. However, there is a legality check when the 'Access of
the handler is used (it is always illegal [remember that access-to-subprogram
checks are always at compile-time, and generic bodies use an assume-the-worst
rule, rather than a runtime check]). So there is no need for
additional rules for this case.
However, a similar example for pragma Attach_Handler would not have any check.
So we do indeed need assume-the-worst rules banning the use of these pragmas
in a generic bodies, and a recheck in private parts of generic specifications.
There is a slight incompatibility with this change, but any such handler
would have to be unused (as taking 'Access in the body is illegal, as the type of
the access is declared outside of the generic unit). That's a highly unlikely
situation; the pragma should be removed in that case.
(2)
This really is a question about evaluating an access-to-protected-subprogram for a
protected object that no longer exists. (That is, it is a general issue). The
accessibility check does not help here (like it does for regular access-to-subprograms),
because the protected object could be allocated for a library-level access type and
then deallocated with Unchecked_Deallocation. This example shows that the interrupt case
is not special; the problem can happen for any access-to-protected-subprogram.
13.11.2(16) says that evaluating a name that denotes a nonexistent object is erroneous.
The question then becomes "does an access-to-protected-subprogram denote an object?" It's
possible to argue that either way (an object is associated with the access value, but
the access value clearly denotes a protected subprogram, which is not an object).
Therefore, we add a rule covering this case.
!corrigendum 13.11.2(16)
Replace the paragraph:
Evaluating a name that denotes a nonexistent object is erroneous. The execution of a call
to an instance of Unchecked_Deallocation is erroneous if the object was created other
than by an allocator for an access type whose pool is Name'Storage_Pool.
by:
Evaluating a name that denotes a nonexistent object or a protected subprogram whose
associated object is nonexistent is erroneous. The execution of a call to an instance
of Unchecked_Deallocation is erroneous if the object was created other than
by an allocator for an access type whose pool is Name'Storage_Pool.
!corrigendum C.3.1(7/2)
Replace the paragraph:
The Attach_Handler pragma is only allowed immediately within the protected_definition
where the corresponding subprogram is declared. The corresponding
protected_type_declaration or single_protected_declaration shall be a
library-level declaration.
by:
The Attach_Handler and Interrupt_Handler pragmas are only allowed immediately
within the protected_definition where the corresponding subprogram is declared.
The corresponding protected_type_declaration or single_protected_declaration
shall be a library-level declaration, and shall not be declared within a
generic body. In addition to the places where Legality Rules normally apply
(see 12.3), these rules also apply in the private part of an instance of a
generic unit.
!corrigendum C.3.1(8/2)
Delete the paragraph:
The Interrupt_Handler pragma is only allowed immediately within the protected_definition
where the corresponding subprogram is declared. The corresponding
protected_type_declaration or single_protected_declaration shall be
a library-level declaration.
!ACATS test
(1) Add a B-Test to check that nested uses of the pragmas are detected in instances,
and that uses in generic bodies are illegal.
(2) Erroneous cases are not usefully testable.
!appendix
From: Pascal Leroy
Date: Wednesday, October 25, 2006 3:05 AM
AI95-00303 removed the last sentence of C.3.1(8) on the ground that it was
a contract model violation.
However, the last sentence of the new C.3.1(8/2) reads:
"The corresponding protected_type_declaration or
single_protected_declaration shall be a library-level declaration."
I don't understand what this sentence is trying to do. First, now that we
have allowed objects in nested scope, why do we restrict the location of
the type declaration? Second, why do we handle
single_protected_declarations differently from objects of a named
protected type? Third, this rule is also a contract model violation, as
shown by the following example:
generic
package G is
pragma Elaborate_Body;
end G;
package body G is
protected type Handler is
procedure My_Handler;
pragma Interrupt_Handler (My_Handler);
end Handler;
...
end G;
If G is a library unit, then apparently type Handler is legal, but what
happens if G is instantiated in a nested scope? If we really need the
last sentence of C.3.1(8/2), then we would also need an assume-the-worst
rule in generic bodies, and presumably we would need rechecking in the
private part of instances.
This is all rather confused. I am starting to wonder if we really need
the last sentence of C.3.1(8/2), or if we should have erased it, too...
****************************************************************
From: Tucker Taft
Date: Wednesday, October 25, 2006 4:54 AM
...
> I don't understand what this sentence is trying to do. First, now that we
> have allowed objects in nested scope, why do we restrict the location of
> the type declaration?
I think the main reason is that interrupt handlers are
referenced using the type Ada.Interrupts.Parameterless_Handler,
which is a library level access-to-protected-subprogram
type. There is no point in defining a protected type
or singleton as an interrupt handler if its subprograms
are not library level.
>...
> ...Second, why do we handle
> single_protected_declarations differently from objects of a named
> protected type?
In what sense are they handled differently? Since we
require the protected type to be library level, and since
a singleton combines the declaration of the type and the
object, naturally we would require a singleton protected
object to also be declared at the library level.
> ...
> Third, this rule is also a contract model violation, as
> shown by the following example:
>
> generic
> package G is
> pragma Elaborate_Body;
> end G;
>
> package body G is
> protected type Handler is
> procedure My_Handler;
> pragma Interrupt_Handler (My_Handler);
> end Handler;
> ...
> end G;
>
> If G is a library unit, then apparently type Handler is legal, but what
> happens if G is instantiated in a nested scope? If we really need the
> last sentence of C.3.1(8/2), then we would also need an assume-the-worst
> rule in generic bodies, and presumably we would need rechecking in the
> private part of instances.
I would presume that the (admittedly unstated) mechanism
would match that associated with 'Access, since that is
the point of the rule in the first place. In other words,
there is a run-time check, in the same way there is a
run-time check for 'Access in a generic. Clearly this
needs to be explicitly stated somewhere in the RM.
> This is all rather confused. I am starting to wonder if we really need
> the last sentence of C.3.1(8/2), or if we should have erased it, too...
I think the requirement that interrupt handler subprograms
be library level is a reasonable one, and we expressed
no intention of changing that as far as I know. One important
reason is that we didn't particularly want to get into
the business of worrying about static links or global displays
at the interrupt level.
****************************************************************
From: Tucker Taft
Date: Wednesday, October 25, 2006 5:19 AM
> I would presume that the (admittedly unstated) mechanism
> would match that associated with 'Access, since that is
> the point of the rule in the first place. In other words,
> there is a run-time check, in the same way there is a
> run-time check for 'Access in a generic. Clearly this
> needs to be explicitly stated somewhere in the RM.
Alternatively, we leave it as is, simply as an early
error check which prevents using an Interrupt_Handler
pragma on a clearly inappropriate protected type.
We then rely on the run-time check when the actual
<Handler>'Access is taken to provide the necessary
check inside generic bodies.
****************************************************************
From: Pascal Leroy
Date: Wednesday, October 25, 2006 5:46 AM
> I think the main reason is that interrupt handlers are
> referenced using the type Ada.Interrupts.Parameterless_Handler,
> which is a library level access-to-protected-subprogram
> type. There is no point in defining a protected type
> or singleton as an interrupt handler if its subprograms
> are not library level.
I disagree. Surely the evaluation of 'Access will fail (at compile-time
or at run-time) but there is absolutely no reason for rejecting the type
declaration.
Imagine that I am using this neat 3rd party generic that does all sorts of
useful things, and also happens to have an interrupt handler in its
specification. I am not interested in interrupt handling (this is just a
capability that is provided by this component, but I don't use it) so I
swear that I will never evaluate 'Access (either directly or through some
code path in the body).
Now you are telling me that I have to instantiate this generic at library
level because of the presence of this type that I will never use. This
might have a big an impact on storage management, on finalization and what
not. This is just the kind of non-orthogonality that I hate, and the
restriction looks entirely arbitrary to me, it so I insist that we should
lift it.
****************************************************************
From: Tucker Taft
Date: Wednesday, October 25, 2006 7:59 AM
That seems a bit harsh, since this restriction has existed
for a long time. I think if you look at it from the opposite
point of view, why should I not find out that I did something
stupid when I declare a protected type with an interrupt
handler at a nested scope? The case of the generic seems
a bit far-fetched to me, and you could certainly move the
protected type into a child package if necessary to allow
the rest of the generic package to be instantiated at
a nested level.
In any case, you originally asked for the rationale, and I
think it was the desire to catch the error earlier. It was
probably also partially to be consistent with the restrictions
on pragma Attach_Handler. It seems if you relax the rules
for pragma Interrupt_Handler you should probably also relax
the rules for Attach_Handler, since at least originally,
Attach_Handler had *fewer* restrictions than Interrupt_Handler.
****************************************************************
From: Pascal Leroy
Date: Wednesday, October 25, 2006 8:42 AM
> I think if you look at it from the opposite
> point of view, why should I not find out that I did something
> stupid when I declare a protected type with an interrupt
> handler at a nested scope?
But then remember what we did in AI 303: we originally had this
restriction for types *and* objects. We noticed that, for objects, it was
creating contract model problems, so we lifted the restriction for objects
but not for types. I claim that this is strangely inconsistent. You can
do something stupid by declaring the protected *object* in a nested scope,
but we will make sure that you don't declare the *type* in a nested scope.
Every rule in the language has a cost for implementers (this is especially
true for the assume-the-worst rules, which tend to be complicated). What
exactly are the benefits to the user here? You keep talking about early
error detection, but in most cases compile-time error detection will be
provided by the rules on 'Access; and in the more pathological situations
(like occurrences in generic bodies) we through away compile-time error
detection anyway when we wrote AI 303.
****************************************************************
From: Tucker Taft
Date: Wednesday, October 25, 2006 11:11 AM
I see your point, but I think there would be an advantage
for having the *same* rules for Interrupt_Handler and Attach_Handler,
as I see a typical maintenance-time operation of switching between
the two. For Attach_Handler there is no 'Access providing the
"fall back" check, so for that one we need rules that provide the
appropriate check. What do we want the rule for Attach_Handler
to be, and how do we deal with Attach_Handlers that occur
in generic bodies? It seems that if we answer that question,
then making the rule for Interrupt_Handler identical would
not be creating any implementation burden, and would keep things
"orthogonal".
****************************************************************
From: Tucker Taft
Date: Wednesday, October 25, 2006 11:24 AM
In talking about pragma Attach_Handler, I am a bit curious what
Interrupts.Current_Handler is going to return when the object is not
library level. This seems like an access-to-subprogram
value that is *not* a legitimate value of the library-level
Parameterless_Handler type, and represents a dangling
reference possibility. I am wondering whether we
should consider changing Interrupts.Current_Handler to
be a bounded error when used to return the value designating
a pragma-attach-handler handler associated with a nested object.
And in any case we should make it erroneous to use the value
after the associated object no longer exists (perhaps that is
a general rule, since you could do an unchecked_deallocation
on a library-level object with a dynamically-attached
handler).
****************************************************************
Questions? Ask the ACAA Technical Agent