!standard D.7(7) 11-01-28 AI05-0224-1/02 !standard D.7(10.3/2) !class binding interpretation 10-10-21 !status Amendment 2012 11-03-11 !status ARG Approved 7-0-0 11-02-20 !status work item 10-10-21 !status received 10-06-07 !priority Low !difficulty Medium !qualifier Error !subject No_Task_Allocators and classwide returns !summary Restrictions No_Task_Allocators and No_Protected_Type_Allocators can be enforced at runtime for allocators of class-wide types. !question How does the No_Task_Allocators restriction interact with a function that returns Some_Limited_Type'Class, as in type Ref is access Some_Limited_Type'Class; type T is new Some_Limited_Type with record Tsk : Some_Task_Type; end record; function F return Some_Limited_Type'Class is begin return X : T; end F; Ptr : Ref := new Some_Limited_Type'Class'(F); !wording Add after D.7(7): In the case of an initialized allocator of an access type whose designated type is class-wide and limited, a check is made that the specific type of the allocated object has no task subcomponents. Program_Error is raised if this check fails. Add after D.7(10.3/2): In the case of an initialized allocator of an access type whose designated type is class-wide and limited, a check is made that the specific type of the allocated object has no protected subcomponents. Program_Error is raised if this check fails. !discussion Attempting to enforce these restrictions at compile-time for class-wide types would prevent allocators for many types that have no task or protected type components. Enforcing the restrictions with a post-compilation check would be very complex (an error would only occur if a type T has task or protected components AND there is an allocator of a class-wide type that covers T; the allocator and the type T are likely to be in different units). Thus we adopt a runtime check. !corrigendum D.7(7) @dinsa @xhang<@xterm There are no @fas for task types or types containing task type subcomponents.> @dinst @xindent of an access type whose designated type is class-wide and limited, a check is made that the specific type of the allocated object has no task subcomponents. Program_Error is raised if this check fails.> !corrigendum D.7(10.3/2) @dinsa @xhang<@xterm There are no @fas for protected types or types containing protected type subcomponents.> @dinst In the case of an initialized @fa of an access type whose designated type is class-wide and limited, a check is made that the specific type of the allocated object has no protected subcomponents. Program_Error is raised if this check fails. !ACATS Test Create an ACATS like the example in the question, to verify that the checks are made. !ASIS No impact on ASIS. !appendix From: Steve Baird Sent: Monday, June 7, 2010 2:20 PM How does the No_Task_Allocators restriction interact with a function that returns Some_Limited_Type'Class, as in type Ref is access Some_Limited_Type'Class; type T is new Some_Limited_Type with record Tsk : Some_Task_Type; end record; function F return Some_Limited_Type'Class is begin return X : T; end F; Ptr : Ref := new Some_Limited_Type'Class'(F); Does this restriction need a dynamic rule to deal with the cases like this? **************************************************************** From: Randy Brukardt Sent: Monday, June 7, 2010 2:34 PM I don't think it *has* to have a dynamic rule, since this clearly (!) can be checked at link-time, and restrictions are technically post-compilation checks anyway. The better question in my view is whether we intend implementations to do the link-time handstands this requires (and if so, why). (You need to do the check at link-time as the body of F probably is separately compiled from the allocator, and you need to know about both in order to show a problem.) **************************************************************** From: Gary Dismukes Sent: Monday, June 7, 2010 3:13 PM > I don't think it *has* to have a dynamic rule, since this clearly (!) > can be checked at link-time, and restrictions are technically > post-compilation checks anyway. But there are cases that might not be checkable at link time, such as when the function has multiple returns, only some of which have task parts. **************************************************************** From: Randy Brukardt Sent: Monday, June 7, 2010 3:30 PM > But there are cases that might not be checkable at link time, such as > when the function has multiple returns, only some of which have task > parts. I don't think that people who want to enforce this particular restriction (which is intended to simplify the runtime system) care whether there is *actually* a task allocator; I think they only care that there *might be* a task allocator. After all, you could put if False then around the allocator itself -- but that would still violate the restriction. So it makes sense to me that any possibility of returning a task from the function would be flagged if the function is subsequently used in an allocator. Specifically, I don't see much difference between: if False then Ptr := new Some_Type_Containing_a_Task; end if; and Ptr := new Root'Class (F); function F return Root'Class is begin if False then return Obj:T; else return Obj2:S; end if; end F; (where type T has a task and type S does not). And the point that I recalled above (that the restriction is intended to simplify the runtime system) implies that a runtime check is inappropriate. A more interesting question is what do implementations currently do in this case? We ought to follow practice if there is in fact any to follow. **************************************************************** From: Steve Baird Sent: Monday, June 7, 2010 3:34 PM > But there are cases that might not be checkable at link time, such as > when the function has multiple returns, only some of which have task parts. Right. Other cases include dispatching calls and calls through access-to-function values. This is not, in general, checkable at link time. **************************************************************** From: Steve Baird Sent: Monday, June 7, 2010 3:39 PM I think that if you want to check this restriction completely without runtime checks, then you would have to be conservative and reject some allocators (e.g., the one in my example) on the grounds that the type of the allocated object *might* have a task part. **************************************************************** From: Randy Brukardt Sent: Monday, June 7, 2010 6:51 PM Right. The current check is conservative anyway: there is no guarantee that the allocator of a task will be executed, but the program is rejected simply by its existence. The only question I would have is "how conservative"? I would think that banning allocators of all limited class-wide types would be going too far, so some sort of post-compilation check would be needed. Perhaps simply banning allocators of a limited class-wide type where any extension has a task component would be adequate (that would be somewhat easier to implement than trying to figure out for every function whether it could return a task). In any case, I think we'll need some additional wording for this rule (whatever we decide). **************************************************************** From: Steve Baird Sent: Thursday, October 21, 2010 4:47 PM Before trying to come up with wording for this recently-created AI, I need to know what rule we want to express. !question How does the No_Task_Allocators restriction interact with a function that returns Some_Limited_Type'Class, as in type Ref is access Some_Limited_Type'Class; type T is new Some_Limited_Type with record Tsk : Some_Task_Type; end record; function F return Some_Limited_Type'Class is begin return X : T; end F; Ptr : Ref := new Some_Limited_Type'Class'(F); Possible solutions include: 1) If the restriction is given, then conservatively reject all access-to-limited-classwide allocators on the grounds that they might violate the restriction. 2) A post-compilation check that if the restriction is given and an allocator exists which allocates an object of type Some_Limited_Type'Class, then there exist no extensions of Some_Limited_Type which have task subcomponents. Or something like that. 3) A runtime check that raises Program_Error if the restriction is in force and the program attempts to allocate a task (via some mechanism such as the given example which makes it past the static checks). I'd like to get a consensus on what we want here so that I can try to have wording for it before the Fairfax meeting. Opinions? P.S. My preference order is #2, #1, #3. **************************************************************** From: Bob Duff Sent: Thursday, October 21, 2010 5:13 PM > Before trying to come up with wording for this recently-created AI, It seems to be so recent that it's in the future. That is, I don't see it on the ada-auth web site. And I've never heard of it. So I'm kind of flying blind here, but... > P.S. My preference order is #2, #1, #3. Hmm. I think #3 is best. #1 is no good -- why should limited types be punished just because they MIGHT contain tasks? #2 is a big pain for implementers, for little gain. **************************************************************** From: Steve Baird Sent: Thursday, October 21, 2010 5:25 PM >> Before trying to come up with wording for this recently-created AI, > > It seems to be so recent that it's in the future. > That is, I don't see it on the ada-auth web site. > And I've never heard of it. > Yeah, this one just landed in my lap today, but if you've seen the !question section, then you've seen the whole AI at this point. > So I'm kind of flying blind here, but... > >> P.S. My preference order is #2, #1, #3. > > Hmm. I think #3 is best. Thanks for taking the time to reply. I don't feel strongly about it, but my idea was that implementors of a runtime system which is built for use with this restriction don't want to deal with this issue at all. They want to know statically that task allocation can't happen. **************************************************************** From: Bob Duff Sent: Thursday, October 21, 2010 5:40 PM > Thanks for taking the time to reply. You're welcome. > I don't feel strongly about it, but my idea was that implementors of a > runtime system which is built for use with this restriction don't want > to deal with this issue at all. They want to know statically that task > allocation can't happen. Yes, I agree with that. But replacing every "create some tasks" with "raise.." seems easy, and accomplishes that goal, while all kinds of link-time checks seems hard. **************************************************************** From: Randy Brukardt Sent: Thursday, October 21, 2010 5:58 PM ... > > Before trying to come up with wording for this recently-created AI, > > It seems to be so recent that it's in the future. > That is, I don't see it on the ada-auth web site. > And I've never heard of it. It was created out of mail from this list on June 7th. So you *ought* to have heard of it (but you didn't comment on it at the time). **************************************************************** From: Randy Brukardt Sent: Thursday, October 21, 2010 6:06 PM ... > > I don't feel strongly about it, but my idea was that implementors of > > a runtime system which is built for use with this restriction don't > > want to deal with this issue at all. They want to know statically > > that task allocation can't happen. > > Yes, I agree with that. But replacing every "create some tasks" > with "raise.." seems easy, and accomplishes that goal, while all kinds > of link-time checks seems hard. Humm. I guess I don't see this, as it seems unlikely to me that the check could be performed outside of the task supervisor. So quite a bit of the overhead of creating tasks would be present in the generated code, even if it wasn't used. That's not the point. Specifically, I don't see how the function F in Steve's example could be compiled without the overhead of being able to create tasks, because there is no way for it to know whether it is called from an allocator (which would raise an exception) or from an object initialization (which would work - at least in the absence of other restrictions). And that means that the call site would need at least some of the overhead of task creation in order to be able to meet the calling convention of the function (since it couldn't know if a task was actually created, and no exception could be raised if it was not). Indeed, that seems to be a problem for *any* version of this restriction other than the over-the-top #1. Not sure what to do here... **************************************************************** From: Steve Baird Sent: Friday, November 19, 2010 6:10 PM Here are some missing pieces for this AI (it currently only has !question and !appendix sections). -- Steve ==== !subject No_Task_Allocators and classwide returns ---- !summary No_Task_Allocators restriction also implies a runtime check. ---- !question (append to end of existing !question section): (task allocation is detected at runtime and Program_Error is raised). ---- !wording Append to the end of D.7(7): This rule is enforced via a post-compilation check. In addition, a (runtime) check is performed as part of the evaluation of any allocator of a type having a class-wide limited designated type; a check is made that the specific type of the allocated object has no task subcomponents. Program_Error is raised if this check fails. This check is performed before any task subcomponents of of the function result object are activated. A runtime check may be needed in the case of an allocated object which is initialized by a function call with a limited class-wide result type. In this case, the check may be performed at the point of the return statement within the function with Program_Error being raised at the point of the call in much the same way that constraint checks may be performed "early" as described in section 6.5's "Implementation Permissions". ---- !discussion This AI does not address the general question of how restriction pragmas interact with conditional expressions. If it is determined that something like type Root is tagged limited null record; type Ref is access Root'Class; type Has_Tasks is new Root with Tsk : Some_Task_Type; end record; Ptr : Ref := new Root'Class'(if Flag then Root'(...) else Has_Tasks'(...)); makes it past the post-compilation check (assuming that a No_Task_Allocators restriction is given), then the runtime check here will fail if Flag turns out to be False. === Is it ok that all of the wording changes for this AI go into the "static semantics" section, even though we are discussing dynamic semantics and implementation permissions? The description of each restriction in this section is contiguous and it would be awkward to break this one up. **************************************************************** From: Randy Brukardt Sent: Friday, November 19, 2010 9:42 PM ... > In addition, a (runtime) check is performed as part of > the evaluation of any allocator of a type having a > class-wide limited designated type; a check is made > that the specific type of the allocated object has no task > subcomponents. Program_Error is raised if this check fails. > This check is performed before any task subcomponents of > of the function result object are activated. > > A runtime check may be needed in the case of an allocated > object which is initialized by a function call with a > limited class-wide result type. In this case, the check may > be performed at the point of the return statement within the > function with Program_Error being raised at the point of the call > in much the same way that constraint checks may be performed "early" > as described in section 6.5's "Implementation Permissions". This runtime check seems to have way too much description; it's bordering on verbal diarrhea. No one cares where this check happens, just that it does rather than performing an action that violates the restriction. We decided during the call that we don't care if such a program leaks memory -- it's buggy! Moreover, Max_Tasks merely says "If an implementation chooses to detect a violation of this restriction, Storage_Error should be raised." For this one, detection is not optional, but similar wording can be used: "If an implementation detects a violation of this restriction other than those detected by the Post-compilation checks, Program_Error should be raised." AARM Discussion: A runtime check is needed as part of the evaluation of any allocator of a type having a class-wide limited designated type in case the actual specific type has a task component. We do not say specifically where the check is made or the exception raised, but in no case should a task be allocated before the exception is raised." That seems like enough, and maybe this can be simplified even further. **************************************************************** From: Steve Baird Sent: Monday, November 22, 2010 4:24 PM > For this one, detection is not optional, but similar wording can be used: > > "If an implementation detects a violation of this restriction other > than those detected by the Post-compilation checks, Program_Error > should be raised." This wording is not appropriate because detection is not optional. For required detection, I think you need to say "a check is performed" in there somewhere. > AARM Discussion: A runtime check is needed as part of the evaluation > of any allocator of a type having a class-wide limited designated type > in case the actual specific type has a task component. We do not say > specifically where the check is made or the exception raised, but in > no case should a task be allocated before the exception is raised." I don't think like the idea of using wording appropriate for an optional check in the RM and then trying to tighten things up with an AARM note. Incidentally, requiring that a task must not be "allocated" (as you suggested) is a tighter requirement than requiring that it must not be "activated" (as I suggested). In the case of a build-in-place function used to initialize an allocator, it sounds like you are requiring that the check must be performed at the point of the return statement, not at the call site. Do you agree, and did you intend this? I certainly want to allow this behavior, but I'm less sure about requiring it. Requiring it does, however, eliminate the need for the second of the two paragraphs that I proposed. We don't need an implementation permission to allow something that is required. So maybe "allocated" is better than "activated" in this case. **************************************************************** From: Randy Brukardt Sent: Monday, November 22, 2010 4:57 PM > > "If an implementation detects a violation of this restriction other > > than those detected by the Post-compilation checks, Program_Error > > should be raised." > > This wording is not appropriate because detection is not optional. > For required detection, I think you need to say "a check is performed" > in there somewhere. The wording "a check" has a specific semantic meaning in Ada, and I don't believe that a restriction can ever cause "a check". Moreover, a check has to occur at a particular place during the execution of the code, and this is precisely what we do not want to require of restrictions. So I don't think the wording "a check" is ever appropriate here. Note that there is no existing restriction which ever uses the wording "a check"; it would be bizarre for this one to be the first. > > AARM Discussion: A runtime check is needed as part of the evaluation > > of any allocator of a type having a class-wide limited designated > > type in case the actual specific type has a task component. We do > > not say specifically where the check is made or the exception > > raised, but in no case should a task be allocated before the exception is raised." > > I don't think like the idea of using wording appropriate for an > optional check in the RM and then trying to tighten things up with an > AARM note. I wasn't trying to "tighten things up", just explain what appears to be necessary and a possible implementation. And I didn't want to use the wording "a check" here, either, but couldn't find another English word which didn't obscure the meaning too much. I probably ought to have put "probably" before "is needed" to make it clear that another implementation is fine. > Incidentally, requiring that a task must not be "allocated" (as you > suggested) is a tighter requirement than requiring that it must not be > "activated" (as I suggested). > In the case of a build-in-place function used to initialize an > allocator, it sounds like you are requiring that the check must be > performed at the point of the return statement, not at the call site. > Do you agree, and did you intend this? The restriction is "No_Task_Allocators" not "No_Tasks_Activated_as_part_of_Allocators". It doesn't make sense to talk about task activation here. (But I admit I did not notice you were talking about activation, other than in general you appeared to over-specify the semantics.) > I certainly want to allow this behavior, but I'm less sure about > requiring it. Requiring it does, however, eliminate the need for the > second of the two paragraphs that I proposed. We don't need an > implementation permission to allow something that is required. > So maybe "allocated" is better than "activated" in this case. I don't see that the wording I gave requires any such thing. We decided that we don't care about memory leakage here, so there is no problem with allocation. If my wording does require that (or anything else for that matter), it is wrong. Depending on the task runtime system, it might very well require that no allocations be done (because there is no code in the RTS to do such allocations). But other runtimes might not care, if the mechanism to create a task is the same for allocators and stack-based objects. I still think you are trying way too hard to over-specify this restriction. I want the wording to be a vague as possible, since we really don't care when or where the violation is detected. (I think it would be OK for an allocated task to start running, so long as the partition never completes normally. Or [more likely] it would be OK for the exception to be raised as soon as the partition starts running, if a post-compilation check has determined that the violation will happen.) Runtime restrictions are overall things vaguely defined, simply because a program that violates a restriction is seriously flawed, and we don't care what they do beyond raising the exception, sometime, somewhere. That goes for *all* restrictions, not just this one. **************************************************************** From: Steve Baird Sent: Monday, November 22, 2010 5:48 PM > The wording "a check" has a specific semantic meaning in Ada, and I > don't believe that a restriction can ever cause "a check". 13.12 says A restriction may impose requirements on the runtime behavior of the program, as indicated by the specification of runtime behavior associated with a violation of the requirement. To me, this clearly means that restriction theoretically could cause a check. I'm not claiming that this is terribly important. > So I don't think the wording "a check" is ever appropriate here. Note > that there is no existing restriction which ever uses the wording "a > check"; it would be bizarre for this one to be the first. What's unique about this restriction is that we seem to have (perhaps inadvertently) broken new ground by adding the rule that runtime checks to detect violations of this restriction are required. Is there any other restriction for which this is the case? I think not. Perhaps we should revisit this decision. We did not discuss the fact that required runtime enforcement of a restriction is something new. If we only require optional enforcement, then I agree with what you have said about this AI. Whatever we decide to do probably needs to be duplicated for No_Proteted_Type_Allocators. **************************************************************** From: Alan Burns Sent: Tuesday, November 23, 2010 2:11 AM Under Ravenscar there is a requirement (expressed as a restriction I think) that Program_Error should be raised if there is a suspension/delay inside a protected object. Without this restriction the situation is a bounded error with 'ignore' being one of the options. So is this not an example of a restriction requiring a run-time check? **************************************************************** From: Randy Brukardt Sent: Tuesday, November 23, 2010 12:33 PM You're thinking of pragma Detect_Blocking (H.5), which is not expressed in the form of a restriction. (And it doesn't use the term "check", either.) Steve said: > What's unique about this restriction is that we seem to have (perhaps > inadvertently) broken new > ground by adding the rule that runtime checks to detect violations of > this restriction are required. > Is there any other restriction for which this is the case? I think not. > Perhaps we should revisit this decision. We did not discuss the fact > that required runtime enforcement > of a restriction is something new. > > If we only require optional enforcement, then I agree with what you > have said about this AI. The problem with that is that this restriction is used in Ravenscar. However, I don't think that is relevant, because Ravenscar also contains No_Task_Hierarchy, which means that a function can never return a task. Thus the situation that causes the problem will never arise. > Whatever we decide to do probably needs to be duplicated for No_Proteted_Type_Allocators. That's actually a different case, because there is nothing in Ravenscar preventing these from being returned from functions. I don't know if that is a hole in Ravenscar (seems possible) or whether it is intended to allow functions to initialize library-level protected objects. I suppose one could argue that No_Local_Protected_Objects disallows a function from returning a protected object, as the return object would momentarily be considered local; but given that these are build-in-place objects its surely not as clear-cut. Alan, is the intent that Ravenscar disallow protected objects returned from functions? Or did no one ever think about it?? **************************************************************** From: Steve Baird Sent: Tuesday, November 23, 2010 12:49 PM > I suppose one could > argue that No_Local_Protected_Objects disallows a function from > returning a protected object, as the return object would momentarily > be considered local; ... One could and I would. "Momentarily" is a somewhat misleading word in the context of an extended return statement. **************************************************************** From: Randy Brukardt Sent: Tuesday, November 23, 2010 1:19 PM True, but we also have to consider a simple return of an aggregate. In that case (since there is no temporary), a local object is invisible. **************************************************************** From: Alan Burns Sent: Wednesday, November 24, 2010 3:24 AM Really we did not think about it - we were more concerned about threads - are there any issues that would want us to disallow from Ravenscar? I guess silly code could create an arbitrary number of objects, but that seems more an issue for dynamic creation of objects anyway and might be part of the restrictions for the sequential part of any complete language subset. **************************************************************** From: Steve Baird Sent: Friday, January 28, 2011 3:10 PM Based on yesterday's phone meeting, I now suggest that the !wording section of this AI consist of only a prefix of what I previously suggested. We should retain only: Add after D.7(7): In the case of an initialized allocator of an access type whose designated type is class-wide and limited, a check is made that the specific type of the allocated object has no task subcomponents. Program_Error is raised if this check fails. Add after D.7(10.3/2): In the case of an initialized allocator of an access type whose designated type is class-wide and limited, a check is made that the specific type of the allocated object has no protected subcomponents. Program_Error is raised if this check fails. Please ignore the subsequent wording changes that were previously proposed, beginning with Add after D.7(19.1/2) The dynamic checks associated with the No_Task_Allocators and No_Protected_Type_Allocators described above ... and including the associated AARM note. ****************************************************************