!standard 7.3.2(10/3) 14-12-04 AC95-00265/00 !standard 7.3.2(12/3) !class confirmation 14-12-04 !status received no action 14-12-04 !status received 14-11-24 !subject Type invariant rules !summary !appendix From: Jeff Cousins Sent: Monday, November 24, 2014 12:29 PM However I try to parse the bit of 7.3.2 12/3 that says "from a descendant of T (including T itself) to an ancestor of type T (other than T itself)" it doesn't make sense to me, in particular "an ancestor of type T (other than T itself)". It can't mean an ancestor of T as that wouldn't have parts of type T. And what is the difference between 'T' and "T itself"? I could understand "from a descendant of T (excluding T itself) to an ancestor that is also a descendent of T (including T itself)" or simply "from a descendant of T towards T". What is meant? And re 7.3.2 10/3 "After successful default initialization of an object of type T, the check is performed on the new object;" Why isn't it "After successful default initialization of an object with a part of type T, the check is performed on the part(s) of type T;" in a similar manner to the other paragraphs? (Convenient though it is to avoid the check). Apologies if it's obvious the rest of you, but I'm confused. *************************************************************** From: Tucker Taft Sent: Monday, November 24, 2014 1:41 PM > However I try to parse the bit of 7.3.2 12/3 that says "from a > descendant of T (including T itself) to an ancestor of type T (other > than T itself)" it doesn't make sense to me, in particular "an ancestor of > type T (other than T itself)". > It can't mean an ancestor of T as that wouldn't have parts of type T. > And what is the difference between 'T' and "T itself"? To an ancestor of type T other than T itself means a "proper" ancestor of T. "Ancestor" includes T and all its parents, progenitors, grandparents, etc. We wanted to exclude cases where the target of the view conversion is T itself. Note that a *view* conversion doesn't change or copy the underlying value, it merely "views" it as the target type. So if the value started out as a descendant of T, then it does still have, at run-time, a part that is of type T. > I could understand "from a descendant of T (excluding T itself) to an > ancestor that is also a descendent of T (including T itself)" or simply > "from a descendant of T towards T". > What is meant? The point is that when you "cross" over T via a view conversion, we want to be sure that any change that is made to the underlying object doesn't violate the invariant for T. Note that the invariant of T might depend on parts that are inherited from some ancestor, so it is possible to mess up the invariant of T without seeing all of T's components. > And re 7.3.2 10/3 "After successful default initialization of an object of > type T, the check is performed on the new object;" > Why isn't it "After successful default initialization of an object with a > part of type T, the check is performed on the part(s) of type T;" in a > similar manner to the other paragraphs? (Convenient though it is to avoid > the check). "Object" includes components of other objects, so when you default initialize a composite object, you *might* be default initializing some of the components as well (or you might be initializing them by an assignment to the component, which is different). So even if an enclosing object is default initialized, an individual component by not be default initialized -- it might be explicitly initialized. E.g.: type Rec is record X : T := Y; end record; D : Rec; -- default initializes D, but explicitly initializes the object D.X. *************************************************************** From: Randy Brukardt Sent: Monday, November 24, 2014 3:03 PM > However I try to parse the bit of 7.3.2 12/3 that says "from a > descendant of T (including T itself) to an ancestor of type T (other > than T itself)" it doesn't make sense to me, in particular "an > ancestor of type T (other than T itself)". > It can't mean an ancestor of T as that wouldn't have parts of type T. > And what is the difference between 'T' and "T itself"? > I could understand "from a descendant of T (excluding T > itself) to an ancestor that is also a descendent of T (including T > itself)" or simply "from a descendant of T towards T". > What is meant? Precisely what is said. This is a very complex check as it only applies to reverse view conversions; there's no way for the wording to ever make any sense without deconstructing it to its various pieces. We've reviewed this multiple times and always determined that it is exactly correct. Tucker explained some of the "why", but mainly I'd suggest that it just "is" and avoid worrying about it. This is just one of the weird checks needed to plug the worst holes in the Type Invariant coverage. > And re 7.3.2 10/3 "After successful default initialization of an > object of type T, the check is performed on the new object;" > Why isn't it "After successful default initialization of an object > with a part of type T, the check is performed on the > part(s) of type T;" in a similar manner to the other paragraphs? > (Convenient though it is to avoid the check). "Object" includes components and subcomponents -- pretty much anything is an object. So we don't need to separately talk about "parts". (Tucker notes that we'd get the wrong answer if we talked about parts, too.) Remember that this rule applies to any default initialization that happens anywhere in the language, including things like <> in an aggregate and the subtype name in an extension aggregate. It's not specific to stand-alone objects, indeed that's the least likely case. The other rules are talking about parameters and the like; those are monolithic things (a component of a parameter is not a parameter, etc.) and thus we explicitly have to talk about the parts. *************************************************************** From: Jeff Cousins Sent: Monday, November 24, 2014 3:44 PM Many thanks for getting back Tuck and Randy. I think I'm seeing it now. So in the following: package T_Ancestor_Package is pragma Assertion_Policy (Check); type T_Ancestor is tagged record A : Integer; end record; function Init return T_Ancestor; end T_Ancestor_Package; with T_Ancestor_Package; package T_Package is pragma Assertion_Policy (Check); type T is new T_Ancestor_Package.T_Ancestor with private with Type_Invariant => Increasing (T); function Increasing (My_T : in T) return Boolean; function Init return T; private type T is new T_Ancestor_Package.T_Ancestor with record B : Integer; end record; end T_Package; with T_Ancestor_Package; with T_Package; procedure T_Package_Test is pragma Assertion_Policy (Check); My_T_Ancestor : constant T_Ancestor_Package.T_Ancestor := T_Ancestor_Package.Init; My_T : T_Package.T; begin T_Ancestor_Package.T_Ancestor (My_T) := My_T_Ancestor; end T_Package_Test; The view conversion is from My_T, whose type is "a descendant of T (including T itself)" as its type is T, to type T_Ancestor, whose type is "an ancestor of type T (other than T itself)". So My_T gets the subset of its components that are also in My_T_Ancestor updated. Then the invariant check is performed on the updated My_T. I'm happier that I asked rather than letting it niggle me though. Thanks again, ***************************************************************