!standard 13.11.2(16) 06-12-15 AI05-0033-1/01 !standard C.3.1(8/2) !class binding interpretation 06-12-15 !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 nested 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 is 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 applies also 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 would not be allowed if the instance is not at the library level. The protected types can be declared in the private part in that case. 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) Add to 13.11.2(16): Evaluation of a dereference denoting a protected subprogram is erroneous if the associated protected object no longer exists. [I don't think this is quite right! But it's close to the original wording for ordinary access types. Better ideas welcome. - RLB] !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. That 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 C.3.1(7/2) !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 '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). ****************************************************************