!standard 13.1.1(16/3) 16-04-21 AI12-0182-1/02 !class ramification 16-04-21 !status Amendment 1-2012 16-07-21 !status WG9 Approved 16-10-08 !status ARG Approved 9-0-1 16-06-11 !status work item 16-02-24 !status received 16-02-04 !priority Low !difficulty Easy !subject Pre'Class and protected operations !summary Class-wide aspects cannot be given for protected operations or task entries, regardless of whether the protected type or task type inherits from an interface. !question Protected operations (both entries and subprograms) do not appear to be primitive operations of the protected type, as they don't meet any of the qualifications of 3.2.3(2-7) [which defines primitive subprograms]. 13.1.1(16/3) requires that an aspect ending with 'Class be given only on tagged types or primitive subprograms of a tagged type. Thus, Pre'Class is not allowed on a protected operation, regardless of whether the protected type is tagged or whether the operation implements some primitive subprogram of the type (inherited from a progenitor). That poses a problem for protected operations, as Pre'Class is "or"ed with any inherited Pre'Class, while Pre (which is legal) is effectively "and"ed. A strict follower of LSP therefore cannot use Pre unless no Pre'Class is inherited. Since a wrapper subprogram would not be allowed if the Synchronization_Kind was By_Entry or By_Protected_Subprogram (as the wrapper is neither of these things), we have a problem. Should Pre'Class be allowed in these contexts? (No.) !response The problem can always be worked around by introducing additional interfaces. Any class-wide preconditions can be placed on such an additional interface, such that it will be inherited by the protected operations. Such an interface can have the appropriate Synchronization_Kind (unlike a wrapper subprogram), and it would not have any semantic effect on operations of existing interfaces (the same operations, calls, and uses would be legal). Since tagged protected types is a rarely used feature, and there is a workaround whose main drawback is extra verbosity, we do not make a language change at this time. !discussion Note that the same issues as described in the question apply to task entries. Any attempt to fix this issue has to be aware of interactions with 6.1.1(7/4) (from AI12-0113-1). The effect of that AI is to only allow primitive and class-wide operations of the type in Pre'Class expressions. Again, since protected operations are not primitive, they would not be allowed in a Pre'Class expression unless the operation was inherited from an interface. Thus, a simple rule allowing Pre'Class and Post'Class to be specified by protected operations and task entries that implement an interface operation would have a significant useability drawback (any routines specific to this particular concrete implementation couldn't be used in a class-wide pre- or postcondition). It also would mean that the Legality of Pre'Class on a protected operation could easily change during program maintenance (as primitive routines are added, deleted, or changed). Such a rule doesn't appear to be much of an advantage over the current situation. One could allow Pre'Class and Post'Class to be specified by protected operations and task entries of any synchronized tagged type. But for that to work cleanly, we'd also need to somehow allow them in the class-wide pre- or postcondition expression. That does not look easy. !ASIS No ASIS effect. !ACATS test We need B-Tests to check that Pre'Class and Post'Class are not allowed for protected operations and task entries; those are likely included in the detailed objectives for 6.1.1 (which already have been created). !appendix From: Randy Brukardt Sent: Thursday, February 4, 2016 5:00 PM I'm trying to figure out precisely where Pre'Class and Post'Class are legal, so I can create tests to check that. (It also has a bearing on how important various other objectives are; as if the only way to involve Pre'Class in a protected operation is via inheritance, tests are less important than if they can be directly specified as well.) All of this discussion also applies to task entries, but I'll avoid mentioning that for the most part to keep this simple(r). I think I've concluded that protected operations (and task entries) do not allow Pre'Class and Post'Class, but it's not crystal-clear. And I don't think it matters for anything other than this rule, so I'm not sure that we ever really answered the question. The important rule is 13.1.1(16.3): If the aspect_mark includes 'Class, then the associated entity shall be a tagged type or a primitive subprogram of a tagged type. Now, it's obvious that if the protected type is not tagged, then Pre'Class and Post'Class are not allowed on any operation. So that's open and shut. However, you can make a protected type tagged by including a progenitor. So that alone doesn't fully answer the question. I tried to apply the Dewar rule to see what we want, and that didn't help either. On one hand, you can't derive from a protected type, so there is no use for inheritance of Pre'Class and Post'Class. Post in particular will work just fine in place of Post'Class. But (on the other hand), Pre is "and"ed with any inherited Pre'Class, so Pre strengthens, not weakens, the precondition. If you are strictly trying to follow LSP and you already have a Pre'Class, you can't use Pre. So that leaves figuring out what operations are primitive. At this point, I think an example is needed: package Int is type Intf is synchronized interface; function Is_OK (Obj : Intf) return Boolean; procedure Proc (Obj : in out Intf) with Pre'Class => Is_OK (Obj); procedure Ent (Obj : in out Intf) with Pre'Class => Is_OK (Obj), Synchronization => By_Entry; end Int; with Int; package Pack is protected type Prot is new Intf with function Is_OK return Boolean; function Is_Wobbly return Boolean; procedure Proc; procedure Proc2; entry Ent; entry Ent2; private ... end Prot; -- function Is_OK (Obj : Prot) return Boolean; -- Inherited here. -- procedure Proc (Obj : in out Prot); -- Inherited here. -- procedure Ent (Obj : in out Prot); -- Inherited here. end Pack; So, first of all, are any of the operations in Prot "primitive subprograms of a tagged type"? Clearly, they're all operations of a tagged type (since Prot is tagged), so that part is handled. What about the primitive subprogram part? 3.2.3 gives the definition of primitive subprograms as: 2 The primitive subprograms of a specific type are defined as follows: 3 The predefined operators of the type (see 4.5); 4 For a derived type, the inherited (see 3.4) user-defined subprograms; 5 For an enumeration type, the enumeration literals (which are considered parameterless functions - see 3.5.1); 6 For a specific type declared immediately within a package_specification, any subprograms (in addition to the enumeration literals) that are explicitly declared immediately within the same package_specification and that operate on the type; 6.1/3 For a specific type with an explicitly declared primitive "=" operator whose result type is Boolean, the corresponding "/=" operator (see 6.6); 7/2 For a nonformal type, any subprograms not covered above [that are explicitly declared immediately within the same declarative region as the type] and that override (see 8.3) other implicitly declared primitive subprograms of the type. ------ We're not interested in operators right now, so paras 3 and 6.1 don't apply, and this surely isn't an enumeration type, so paragraph 5 does not apply. Paragraph 4 applies to the 3 subprograms that I showed commented out, but certainly not to any of the operations declared inside of Prot. Similarly, paragraph 6 doesn't apply (as the protected operations are not declared immediately within the same package specification; they're inside of the protected type instead). Note that it *could* apply to a subprogram declared after the commented out procedure Ent, but that's obvious and the same as any other type so let's not consider that further. That just leaves us with paragraph 7. Clearly, it cannot apply to Is_Wobbly, Proc2, and Ent2 (there is no implicitly declared primitive subprogram that corresponds to those). It seems pretty clear that paragraph 7 doesn't apply to the others, either. It specifically uses "override", and none of these operations override anything; rather they are "implemented by" the corresponding protected operations. (I forget why we used that model, but we did so very intentionally -- I presume there were some ramifications of overriding that were inappropriate for "implemented by" operations. Therefore, none of the protected operations Is_OK, Proc, nor Ent are primitive for the tagged type Prot, and thus none of these operations allow Pre'Class or Post'Class. Question 1: Do you agree? Did I get this analysis right? Question 2: Is this really what we want? In particular, we can't weaken the precondition of Proc. (Proc has an inherited precondition of Is_OK (Obj) from the interface.) For example, let's say that we wanted to eliminate the precondition on Proc. That can't be done (directly): procedure Proc with Pre'Class => True; -- Is illegal by the above analysis procedure Proc with Pre => True; -- Has no effect, the effective precondition is "Is_OK (Self) and True" One could do it by having Is_OK always return True, but that might not be appropriate for some other reason. Of course, I'm assuming that weakening a precondition is actually useful -- that's a premise I doubt in actual fact -- so I don't know if this really matters. In the absence of weakening as being useful, Pre and Post work fine, since no inheritance is possible anyway (since derivation is illegal). In addition, adding an explicit subprogram declaration at the end of the visible part of the package would indeed allow the use of Pre'Class: procedure Proc (Obj : in out Prot) with Pre'Class => True; then renaming the protected operation Proc to Local_Proc and writing a body of: procedure Proc (Obj : in out Prot) is begin Obj.Local_Proc; end Proc; This is really what "implemented by" does anyway, so it's only slightly annoying to write this out. Note that Local_Proc is no longer inheriting a precondition, so we've solved the problem (if there is one). Thus my answers to my own questions are (1) of course!; and (2) dunno, but let's leave it illegal until we hear screaming from actual users. (After all, changing illegal to legal is almost always a compatible change; going in the other direction isn't possible, in general.) I guess I'd like some confirmation before putting in a bunch of hours to write tests for these cases. What say others here? (Or have I put you all to sleep?? ;-) **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, February 4, 2016 11:31 PM > Question 1: Do you agree? Did I get this analysis right? Well, that's exactly what the text says... > Question 2: Is this really what we want? Not even talking about pre/post, it is possible to dispatch to such operations. According to your analysis, it is therefore possible to dispatch to non-primitive operations. Ouch! Something has to be done... **************************************************************** From: Randy Brukardt Sent: Friday, February 5, 2016 12:25 AM I'm unsure if that really matters; it might mess up the 20,000 foot view of Ada a bit, but I doubt that 99% of users would care. And it clearly was intended that "implemented by" is different than "overriding". I think the model of "implemented by" is that there is a compiler-generated primitive operation that calls the non-primitive operation, which certainly isn't unusual (there's no reason that any primitive operation can't call something non-primitive - it happens all the time). So I think the issue is exclusively with Pre/Post (as inheritance is not allowed). **************************************************************** From: Tucker Taft Sent: Thursday, February 11, 2016 3:43 PM > Therefore, none of the protected operations Is_OK, Proc, nor Ent are > primitive for the tagged type Prot, and thus none of these operations > allow Pre'Class or Post'Class. > > Question 1: Do you agree? Did I get this analysis right? Yes. > Question 2: Is this really what we want? No, I think we want to allow Pre'Class and Post'Class on any protected operation that implements a primitive operation. > ... In addition, adding an explicit subprogram declaration at the end of the > visible part of the package would indeed allow the use of Pre'Class: > procedure Proc (Obj : in out Prot) with Pre'Class => True; > then renaming the protected operation Proc to Local_Proc and writing a body of: > procedure Proc (Obj : in out Prot) is > begin > Obj.Local_Proc; > end Proc; > This is really what "implemented by" does anyway, so it's only slightly > annoying to write this out. Note that Local_Proc is no longer inheriting a > precondition, so we've solved the problem (if there is one). But that doesn't work for operations that have a "Synchronized => By_Entry" or "Synchronized => By_Protected_Procedure" since they cannot be implemented by a hand-written wrapper. ... > What say others here? (Or have I put you all to sleep?? ;-) I would say we should allow 'Class aspects on any protected operation that implements a tagged primitive. **************************************************************** From: Randy Brukardt Sent: Thursday, February 11, 2016 4:37 PM >> ... In addition, adding an explicit subprogram declaration at the end of the >> visible part of the package would indeed allow the use of Pre'Class: >> procedure Proc (Obj : in out Prot) with Pre'Class => True; then >> renaming the protected operation Proc to Local_Proc and writing a body of: >> procedure Proc (Obj : in out Prot) is >> begin >> Obj.Local_Proc; >> end Proc; >> This is really what "implemented by" does anyway, so it's only >> slightly annoying to write this out. Note that Local_Proc is no >> longer inheriting a >> precondition, so we've solved the problem (if there is one). >But that doesn't work for operations that have a "Synchronized => By_Entry" or >"Synchronized => By_Protected_Procedure" since they cannot be implemented by a >hand-written wrapper. Good point. But... >> It seems pretty clear that paragraph 7 doesn't apply to the others, either. >> It specifically uses "override", and none of these operations >> override anything; rather they are "implemented by" the corresponding >> protected operations. (I forget why we used that model, but we did so >> very intentionally -- I presume there were some ramifications of >> overriding that were inappropriate for "implemented by" operations. I didn't get this analysis quite right. There is in fact an implicitly declared subprogram that overrides for "implemented by". But that clearly is a *different* subprogram than the original operation; so the current rules allow Pre'Class on the implicit subprogram but not on the original protected operation. The interesting thing is that this implicitly declared subprogram wouldn't be allowed explicitly (as Tuck points out), and that's downright weird, as the semantics (other than the one Legality Rule) are defined completely in terms of an implicit subprogram that you're not allowed to write explicitly. There would clearly be no harm in allowing some explicit subprograms within limits -- but clearly that isn't important enough to mess with. ... > I would say we should allow 'Class aspects on any protected operation > that implements a tagged primitive. Sigh. We always chose the hardest path that makes it impossible to cross off items on my work lists. :-) I presume that the same ought to be true for a task entry that implements a tagged primitive. So you are suggesting that we change 13.1.1(16/3) to something like: If the aspect_mark includes 'Class, then the associated entity shall be a tagged type, a primitive subprogram of a tagged type, or an entry or protected subprogram that implements a primitive subprogram of a tagged type. Right? I take it I get to write up an AI to this effect?? **************************************************************** From: Tucker Taft Sent: Thursday, February 11, 2016 5:26 PM > ... So you are suggesting that we change 13.1.1(16/3) to something like: > > If the aspect_mark includes 'Class, then the associated entity shall > be a tagged type, a primitive subprogram of a tagged type, or an entry > or protected subprogram that implements a primitive subprogram of a > tagged type. > > Right? Right. > > I take it I get to write up an AI to this effect?? Yes, that seems appropriate ;-). **************************************************************** From: Randy Brukardt Sent: Wednesday, February 24, 2016 7:50 PM > > ... So you are suggesting that we change 13.1.1(16/3) to something like: > > > > If the aspect_mark includes 'Class, then the associated entity shall > > be a tagged type, a primitive subprogram of a tagged type, or an > > entry or protected subprogram that implements a primitive subprogram > > of a tagged type. > > > > Right? > > Right. > > > > > I take it I get to write up an AI to this effect?? > > Yes, that seems appropriate ;-). I've tried doing this (it will be AI12-0182-1; I mostly wanted the number so I could put it into my ACATS tests and documents), and in a case of what Steve calls "heat vision", I've got more questions than answers. :-) (1) Are we sure that we want the suggested rule? Consider the original example with some additional preconditions: package Int is type Intf is synchronized interface; function Is_OK (Obj : Intf) return Boolean; procedure Proc (Obj : in out Intf) with Pre'Class => Is_OK (Obj); procedure Ent (Obj : in out Intf) with Pre'Class => Is_OK (Obj), Synchronization => By_Entry; end Int; with Int; package Pack2 is protected type Prot2 is new Intf with function Is_OK return Boolean; function Is_Wobbly return Boolean; procedure Proc with Pre'Class => Is_OK (Prot2); -- (1) procedure Proc2 with Pre'Class => Is_OK (Prot2); -- (2) entry Ent with Pre'Class => Is_OK (Prot2); -- (3) entry Ent2 with Pre'Class => Is_OK (Prot2); -- (4) private ... end Prot2; -- function Is_OK (Obj : Prot2) return Boolean; -- Inherited here. -- procedure Proc (Obj : in out Prot2); -- Inherited here. -- procedure Ent (Obj : in out Prot2); -- Inherited here. end Pack2; With Tucker's originally proposed rule (as worded by me), aspects (1) and (3) are legal, but aspects (2) and (4) are illegal. While this can be explained, I'd say that the usability of this is suspect. One has to look up the definition of the interface type(s) to be able to determine whether Pre'Class/Post'Class are legal. I know I would rather have an all-or-nothing rule for that, rather than having niggle with declaration details. So, I suggest that we consider an alternative replacement for 13.1.1(16/3): If the aspect_mark includes 'Class, then the associated entity shall be a tagged type, a primitive subprogram of a tagged type, or a visible entry or visible protected subprogram of a tagged synchronized type. This allows all of (1)-(4), but disallows 'Class on private/body operations of the protected type. But all is not yet well... (2) The interaction of either of these rules with the (im)famous 6.1.1(7/4) -- the "nominal generic formal type" rule seems problematical (but not fatal). Consider another version of the original example: with Int; package Pack3 is protected type Prot3 is new Intf with function Is_OK return Boolean; function Is_Wobbly return Boolean; procedure Proc with Pre'Class => Is_Wobbly (Prot3); -- (1) procedure Proc2 with Pre'Class => Is_Wobbly (Prot3); -- (2) entry Ent with Pre'Class => Is_Wobbly (Prot3); -- (3) entry Ent2 with Pre'Class => Is_Wobbly (Prot3); -- (4) private ... end Prot3; -- function Is_OK (Obj : Prot3) return Boolean; -- Inherited here. -- procedure Proc (Obj : in out Prot3); -- Inherited here. -- procedure Ent (Obj : in out Prot3); -- Inherited here. end Pack3; All four of (1) .. (4) are illegal by 6.1.1(7/4) -- because the "nominal generic formal type" only has access to primitive and class-wide subprograms of the ancestor type, and Is_Wobbly is not primitive (as discussed by the original version of this question). In the previous Prot2 example, Is_OK does pass 6.1.1(7/4), as is it is an inherited primitive operation of Intf. So this is a weird restriction on the contents of Pre'Class (and Post'Class). The good news is that this isn't fatal, in that one can always create a dummy interface to use a progentitor to make any helper functions that aren't otherwise primitive into primitive functions: with Int; package Pack4 is type Dummy_Intf is limited interface; function Is_Wobbly (Obj : Dummy_Intf) return Boolean; protected type Prot4 is new Intf and Dummy_Intf with function Is_OK return Boolean; function Is_Wobbly return Boolean; procedure Proc with Pre'Class => Is_Wobbly (Prot4); -- (1) procedure Proc2 with Pre'Class => Is_Wobbly (Prot4); -- (2) entry Ent with Pre'Class => Is_Wobbly (Prot4); -- (3) entry Ent2 with Pre'Class => Is_Wobbly (Prot4); -- (4) private ... end Prot4; -- function Is_OK (Obj : Prot4) return Boolean; -- Inherited here. -- function Is_Wobbly (Obj : Prot4) return Boolean; -- Inherited here. -- procedure Proc (Obj : in out Prot4); -- Inherited here. -- procedure Ent (Obj : in out Prot4); -- Inherited here. end Pack4; Of course, this isn't very usable, but perhaps this situation will not come up often?? I'm not much interested in revisiting the rabbit hole that 6.1.1(7/4) was, especially as it is correct in the majority of the cases -- but perhaps we have to?? [I note that we already have in a sense in AI12-0170-1, but for that we're mostly confirming the obvious.] Thoughts?? **************************************************************** From: Tucker Taft Sent: Wednesday, February 24, 2016 8:26 PM I cannot see how we can justify doing anything complex to support this feature. At this point I would suggest one can neither override nor define a 'Class aspect on a protected operation. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 24, 2016 8:50 PM I guess it comes down to how important LSP operation of Pre'Class is on protected operations. If one considers that important, then doing nothing is probably not good. It certainly would be consistent to adopt your original rule and do nothing else. In that case, you could only write Pre'Class on implemented-by routines, and you could only use implemented-by routines in Pre'Class. Usability isn't wonderful, but it would work and cost relatively little in terms of language rules (and hopefully implementation of those rules). The alternative of doing nothing at all is also consistent (we don't have to worry about what one can write in a Pre'Class if you can't write a Pre'Class :-). That leaves the LSP hole with no workaround for By_Entry and By_Protected_Subprogram cases. (One could imagine fixing that by allowing explicit wrappers, since the compiler is generating implicit wrappers with the same parameters -- but that's probably even more complex than the more straightforward fix - since such wrappers would have to be heavily restricted.) I agree that the full fix (which is to allow tagged PTs to use Pre'Class on any protected operation, and allow Pre'Class to use any protected operation) seems to be too complex for a lesser-used part of the Standard. Anyway, I guess we'll have to talk about this, at least a little -- I don't know exactly what I think and it would help to hear from a broader range than the usual suspects here in e-mail. **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, February 25, 2016 4:55 AM > With Tucker's originally proposed rule (as worded by me), aspects (1) > and (3) are legal, but aspects (2) and (4) are illegal. While this can be > explained, I'd say that the usability of this is suspect. One has to > look up the definition of the interface type(s) to be able to > determine whether Pre'Class/Post'Class are legal. I know I would > rather have an all-or-nothing rule for that, rather than having niggle > with declaration details. I don't find this surprising. Contracts and the LSP are for OO stuff, therefore anything related to contracts originates from the interface part. Operations that are /not/ part of an interface are not subject to contracts, like a PT that does not implement any interface. If you really want to be fully consistent, then you'll have to consider all PT as tagged - we certainly don't want to go that way. **************************************************************** From: Tullio Vardanega Sent: Thursday, February 25, 2016 11:54 AM JP has a point on this one (except for the dismissive "stuff" attached to OOP ;-) Moreover, the interaction between OOP inheritance and concurrency (more specifically, synchronization protocols, which are special kind of "operations" to stay on JP's point) is known to be a thorny problem. **************************************************************** From: Randy Brukardt Sent: Thursday, February 25, 2016 3:17 PM Surely, but neither of you actually answered the question. Which is, what to do? I can't test the rule if we don't decide what it is! (1) Do nothing; that effectively means that OOP contracts can *only* be given/modified on interfaces -- one would have to introduce an extra interface to make any (OOP) precondition changes to a tagged synchronized type. (2) Adopt Tucker's rule only; that allows making direct OOP precondition changes, but all of the operations have to be declared in an interface (so an extra interface still might be required). (3) Something else, presumably to get completely get rid of the need for an extra interface. Choosing (1) has the extra advantage that I can immediately write the necessary tests (and it also requires the least objectives in total, because synchronized tagged types don't need any separate testing from interfaces), but surely we shouldn't be guided solely what's best for testing! **************************************************************** From: Jean-Pierre Rosen Sent: Friday, February 26, 2016 2:36 AM > Surely, but neither of you actually answered the question. > > Which is, what to do? I can't test the rule if we don't decide what it > is! I think my comment implied 1)... > (1) Do nothing; that effectively means that OOP contracts can *only* > be given/modified on interfaces But we are talking about pre/post'CLASS here, not simple Pre/Post, right? So yes, I find it quite natural that these are connected to the interface only - since that's the only thing that allows "classes" of POs. > -- one would have to introduce an > extra interface to make any (OOP) precondition changes to a tagged > synchronized type. So what? The alternative is to allow pre/post'Class on things not related to inheritance. Not better if you ask me... **************************************************************** From: Tuillo Vardanega Sent: Friday, February 26, 2016 3:21 AM I am tempted to side with JP on the preference for (1), for the reason he gives. But I am also not fully happy with it, because the language allows in fact POs to derive from interfaces, which justifies reasoning on 'Class for Pre and Post for them. All in all, I suppose the bottom line is to require that one cannot override a 'Class aspect on a protected operation (I am not sure that I would go as far as also disallowing one to define that). **************************************************************** From: Randy Brukardt Sent: Friday, February 26, 2016 4:21 PM ... > > -- one would have to introduce an > > extra interface to make any (OOP) precondition changes to a tagged > > synchronized type. > > > So what? The alternative is to allow pre/post'Class on things not > related to inheritance. Not better if you ask me... Not at all. The proposed alternative (2) is to allow Pre/Post'Class on implemented-by protected operations only (that is, the ones that override interface operations, although the mechanism is formally more than just overriding). Surely those are "related to inheritance". And specifying Pre is very different than Pre'Class if there is already a non-trivial Pre'Class -- I would hope that style guides would not allow mixing of them. Besides, there's no such limitation for "regular" tagged types; you can use Pre/Post'Class on any operation even if there is no intention to ever inherit from them. (I.e. "final" in some other languages.) And one easily could imagine a future version of Ada allowing limited derivation from tagged synchronized types. (The problem being implementation inheritance, not the inheritance of the operations.) It seems wrong to assume that such derivations are forever impossible. I can see the argument that it isn't worth the complication to allow anything for protected operations, as the workaround (introduce extra, otherwise unneeded interfaces) isn't too awful [the main objection is just extra clutter - a LOT of extra clutter] and the feature in general isn't used that much. (Given my personal feelings about interfaces, it's funny that I'm even arguing this... :-) But I don't see any different between any of the options (1, 2, or 3) in relation to inheritance -- they're all related to inheritance, in my view. You don't have to actually inherit something to be related to inheritance, otherwise you are basing language rules on what the programmer intends to write. **************************************************************** From: Tucker Taft Sent: Friday, February 26, 2016 4:37 PM > ... I can see the argument that it isn't worth the complication to > allow anything for protected operations, as the workaround (introduce > extra, otherwise unneeded interfaces) isn't too awful [the main > objection is just extra clutter - a LOT of extra clutter] and the > feature in general isn't used that much.... At this point I think I would vote for only allowing an overriding of 'Class aspects on primitives, and give up on doing it on protected operations. As you point out, you can create multiple synchronized interfaces if it is important to have (effectively) two different 'Class aspects on two (concrete) descendants of the same synchronized interface. That seems adequate for this very special case, and it simplifies our life. If this becomes a hot button, we can always permit it some time down the road. ****************************************************************