Version 1.1 of acs/ac-00265.txt

Unformatted version of acs/ac-00265.txt version 1.1
Other versions for file acs/ac-00265.txt

!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,

***************************************************************


Questions? Ask the ACAA Technical Agent