Version 1.5 of ai12s/ai12-0289-1.txt
!standard 3.10(26) 19-02-18 AI12-0289-1/02
!class ramification 18-08-06
!status Amendment 1-2012 18-11-19
!status WG9 Approved 22-06-22
!status ARG Approved 5-1-1 18-10-21
!status work item 18-08-06
!status received 18-08-03
!priority Low
!difficulty Easy
!subject Implicitly null excluding anonymous access types and conformance
!summary
Conformance strictly enforces whether or not a parameter is null excluding
when it is declared, even through it might change later. In such cases,
an explicit null exclusion is required.
!question
Consider:
package Pack is
type Priv is private;
procedure PPA (X : access Priv); --
private
type Priv is tagged record
Int : Integer := 99;
end record;
end Pack;
package body Pack is
procedure PPA (X : access Priv) is --
begin
null;
end PPA;
end Pack;
The development version of our compiler rejects (B), saying that it does not
fully conform to the specification (A).
This appears to be true for this code. The parameter X of (A) is not a
controlling parameter, and thus it does not exclude null. On the other hand,
the parameter X of (B) is a controlling parameter (since Priv is tagged here),
and thus it does exclude null.
Full conformance requires the subtypes to statically match, and static
matching here is defined by the third sentence of 4.9.1(2/5): "Two anonymous
access-to-object subtypes statically match if their designated subtypes
statically match, and either both or neither exclude null, and either both or
neither are access-to-constant."
This is clearly False, and thus matching fails. Is this intended? (Yes.)
!response
The code given would be legal in Ada 95 (because there is no such thing as
"exclude null" in Ada 95. However, it seems that allowing them to match brings
up semantic issues.
The public specification of PPA (at (A)) clearly does not exclude null. Thus,
a caller could reasonably try to pass null to it. However, the body clearly
does exclude null, and thus may not expect to get null. (Note that this is
true both for user-written code and the code generated by the compiler -
compilers generally omit null checks for null excluding subtypes.)
We could also consider that there is an implicit null exclusion for the
specification. However, this would be a privacy leakage, as a null excluding
subtype would allow a rename like:
procedure Ren (X : not null access Pack.Priv) renames Pack.PPA;
...and there is nothing in the visible part which should allow this renames.
We could imagine having a double-secret null exclusion applying here that
didn't affect renames Legality (but does affect conformance!), but that's
getting crazy complicated for an unlikely case.
Ada requires full conformance so that a specification and the body are
defining precisely the same thing -- that is clearly not the case here if
we were to allow the declarations.
So we conclude that the incompatibility (which is easily fixed by adding
"not null" to the declaration of parameter X in both the spec and the body)
is the least of the possible evils. Explicitly requiring "not null" in this
case is better for readers AND compilers, and is unlikely to cause many
problems.
!wording
Add after AARM 3.10(26.e/2): [Incompatibilities with Ada 95]
Static matching (see 4.9.1) requires that both anonymous access types exclude
null; and full conformance requires statically matching subtypes. Therefore,
an access parameter that designates an untagged private type P (which does
not exclude null) does not match its completion if P is completed with a
tagged type (in that case, the parameter is controlling and thus excludes null,
regardless of whether there is an explicit null exclusion on the body).
!discussion
Existing compilers seem to break privacy in this case, allowing the renames
despite the fact that no null exclusion appears in the specification.
---
The incompatibility can be worked around by adding an explicit null exclusion
to (A) and (B). Note that the definition of full conformance requires explicit
exclusions on both parts.
!ASIS
No ASIS effect.
!ACATS test
An ACATS B-Test should be created to check that the conformance check fails
in examples like the one on the question. (Note that ACATS B413004.A did
this in an unrelated test, but that's been eliminated.) A simple test is
found in !appendix, and could be used.
!appendix
From: Randy Brukardt
Sent: Friday, August 3, 2018 6:12 PM
I'm tracking a regression problem in Janus/Ada, and it makes me wonder if
there is a language problem. The example looks like:
package Pack is
type Priv is private;
procedure PPA (X : access Priv);
private
type Priv is tagged record
Int : Integer := 99;
end record;
end Pack;
package body Pack is
procedure PPA (X : access Priv) is
begin
null;
end PPA;
end Pack;
The up-to-the-minute Janus/Ada compiler rejects the completion of PPA, saying
that the types of the parameter do not match (needed for full conformance).
I've tracked this down to a literal implementation of static matching. The
specification of PPA does not have a controlling parameter, so the access
type there does not exclude null. However, the body of PPA does have a
controlling parameter, so the access type there does exclude null
(implicitly). Since the third sentence of 4.9.1(2/5) is "Two anonymous
access-to-object subtypes statically match if their designated subtypes
statically match, and either both or neither exclude null, and either both or
neither are access-to-constant.", static matching has failed and thus the
error. (Remember that every access_definition makes a new type, so these
parameters are of different types and thus require matching rules.)
I figure to add some code to ignore implicit null exclusions when static
matching (the above error being nonsense, of course). But that doesn't seem
to strictly follow the language definition.
Moreover, there seems to be a dynamic semantic issue associated with this.
The specification of PPA will not have a null exclusion, so it will allow
passing null. But the body will not be expecting to get null. One cannot just
change the specification to be null excluding, because that would be a privacy
leakage -- specifically because it affects legality. Specifically:
procedure Ren (X : not null access Pack.Priv) renames Pack.PPA;
is legal if "X : access Priv" is considered null excluding, and is not legal
if it is not considered null excluding.
Similarly, if one just changes the body to not be null excluding, then the
renames becomes illegal, and we have to deal with the possibility of
dispatching on null (in the body and children of Pack).
One could imagine "fixing" this bizarreness by inventing a subtype that
dynamically excludes null but doesn't "exclude null" for legality checks, then
using that in the specification of PPA. But (besides the substantial amount of
work involved, since it touches almost everything involving exclusions), I
don't see any language reason for such a thing to exist (we require full
conformance specifically so that the specs and bodies have the same properties
- that would seem to be broken here).
The only other thought that I had is that PPA maybe is never supposed to
become a dispatching operation (although that would be different than the
behavior of virtually every other characteristic); in that case, there'd
never be an (implicit) exclusion on X. But the wording in 3.9.2 seems to imply
otherwise (although it depends on when a "type is declared", which is not a
single point!). And such an interpretation would make "tagged" almost
irrelevant in such cases (almost nothing would dispatch).
Any ideas what the intent is here? I'm unsure even what I should be trying to
implement in this case (and I wanted to ship a new compiler by today -
obviously not making it).
****************************************************************
From: Steve Baird
Sent: Friday, August 3, 2018 7:07 PM
> The specification of PPA will not have a null exclusion, so it will
> allow passing null. But the body will not be expecting to get null.
That would indeed be a problem, so it is a good thing that your latest
compiler is correctly rejecting this example.
Wouldn't this example be legal if the procedure declaration included an
explicit null exclusion (even though the completion does not have an
*explicit* null exclusion)?
If so, then it seems like all is well and there is no language problem.
An AARM note confirming that implicit null exclusions participate in
conformance checking might be worthwhile.
If you don't like this resolution, then we could consider tightening up the
conformance rule to require an explicit null exclusion for the procedure body
(as well as for the spec) in cases like this one. In order to conform, the
spec and the body would have to agree in two different ways: the would have to
agree with respect to null exclusions and also with respect to *explicit* null
exclusions. We'd have to discuss the compatibility implications of this
approach.
I think no change is needed, but there may be some implementation issue I am
overlooking that would argue for the tighter conformance rule described above.
****************************************************************
From: Randy Brukardt
Sent: Friday, August 3, 2018 9:00 PM
> > The specification of PPA will not have a null exclusion, so it will
> > allow passing null. But the body will not be expecting to get null.
>
> That would indeed be a problem, so it is a good thing that your latest
> compiler is correctly rejecting this example.
Correctly? GNAT 18.1 allows this example, as it is in a relatively old ACATS
test. Probably so do any other Ada 2005 compilers (it is an Ada 2005 B-Test,
testing that untagged prefixed notation isn't allowed). No one has complained.
> Wouldn't this example be legal if the procedure declaration included
> an explicit null exclusion (even though the completion does not have
> an *explicit* null exclusion)?
Yes, that would be legal (at least as I read the RM).
> If so, then it seems like all is well and there is no language
> problem.
There would be a potential compatibility problem with Ada 95 code, though,
since there is no explicit null exclusion anywhere. So this is legal Ada 95
code, but we'd be rejecting it (of course, in Ada 95 anonymous parameters
are always null excluding, so it doesn't have this issue).
> An AARM note confirming that implicit null exclusions participate in
> conformance checking might be worthwhile.
>
> If you don't like this resolution, then we could consider tightening
> up the conformance rule to require an explicit null exclusion for the
> procedure body (as well as for the
> spec) in cases like this one.
> In order to conform, the spec and the body would have to agree in two
> different ways: the would have to agree with respect to null
> exclusions and also with respect to
> *explicit* null exclusions. We'd have to discuss the compatibility
> implications of this approach.
I'm worried about the compatibility implications of making the original
example illegal; how to fix it isn't a major concern.
> I think no change is needed, but there may be some implementation
> issue I am overlooking that would argue for the tighter conformance
> rule described above.
No, there's no problem if you argue that rejecting this test (and others
like it) is OK. I was assuming that an 11 year old ACATS test is correct,
especially when it clearly was legal in Ada 95.
****************************************************************
From: Tucker Taft
Sent: Saturday, August 4, 2018 12:21 PM
Interesting problem!
I see various solutions:
1) Make it illegal in Ada 2005+ -- require explicit "not null" on visible
declaration.
2) Make it illegal in Ada 2005+ -- require explicit "not null" on both
visible and private declarations.
3) Make it legal, but with the semantics that the body may *not* assume the
parameter is non-null, presuming that a call might come via the
non-dispatching visible declaration, so a call with a null parameter
might actually succeed.
4) Make it legal, but with the semantics that a Constraint_Error is raised
if null is passed as an actual parameter, even in a call via the
non-dispatching visible declaration, so that a call with a null parameter
will always raise an exception.
As far as renaming, (1) and (2) would clearly require "not null" (unless
renaming as a dispatching operation). (3) and (4) would not allow "not null"
if renaming the visible declaration, but would have the same dynamic semantics
as described.
---
I would probably recommend (1). This avoids creating a new kind of
conformance, which (2) would require, and just requires "honesty in
advertising" which is generally a good thing. The incompatibility is
easy to handle for the user -- just add "not null" on the visible routine.
Of course, in "Ada 95 mode" no changes are required. (3) and (4) just seem too
confusing.
****************************************************************
From: Steve Baird
Sent: Monday, August 6, 2018 11:10 AM
> I see various solutions:
...
> I would probably recommend (1).
That is also what I recommended in my earlier message.
I agree with Randy that compatibility is an issue here, but I agree with Tuck
about the set of alternatives that would be reasonable to consider and that #1
is the best of the bunch.
****************************************************************
From: Randy Brukardt
Sent: Monday, August 6, 2018 5:52 PM
OK. I think there is a fairly strong argument that #1 is precisely what the
language wording says. I had wondered if the Dewar rule about nonsense applied
here (because of the compatibility issue), but it sounds like the conclusion
is that the language is correct as written (even if no one other than me - by
accident - actually implemented it that way).
Therefore, I conclude that a Ramification AI is needed to confirm that the
language says #1 and really means that (probably with a AARM Ramification note
somewhere, either 3.10, 4.9.1, or 6.3.1).
As far as the ACATS test goes, since it's not testing anything related to any
of these clauses and it is an Ada 2005 test, I think the best plan is to put
explicit "not null" on both the spec and the body. (Then it won't need
modification regardless of what the result is.)
Someday, we'll need an ACATS test to test that compilers actually implement
#1, but that obviously can wait for the Ada 2020 test suite.
Any objections to this plan??
****************************************************************
From: Randy Brukardt
Sent: Monday, August 6, 2018 8:26 PM
For the record, given the following program:
with Ada.Text_IO;
procedure TestUgh is
-- Tests for GNAT interpretation of AI12-0289-1 problem.
package Pack is
type Priv is private;
procedure PPA (X : access Priv); -- (A)
procedure Ugh;
private
type Priv is tagged record
Int : Integer := 99;
end record;
end Pack;
package body Pack is
procedure PPA (X : access Priv) is -- (B)
begin
null;
end PPA;
procedure Ren (X : not null access Pack.Priv) renames Pack.PPA; -- OK.
type PAC is access all Priv'Class;
procedure Ugh is
V : PAC := null;
begin
PPA (V); -- Dispatching call!
end Ugh;
end Pack;
begin
Ada.Text_IO.Put_Line ("--- TestUgh - Conformance of anonymous access types with implicit null exclusions.");
begin
Pack.PPA (null); -- Appears legal, what happens?
Ada.Text_IO.Put_Line ("?? No exception raised when passing null.");
exception
when Constraint_Error =>
Ada.Text_IO.Put_Line ("?? Contraint_Error raised when passing null");
end;
begin
Pack.Ugh; -- Try to dispatch on null.
Ada.Text_IO.Put_Line ("** No exception raised when dispatching on null.");
exception
when Constraint_Error =>
Ada.Text_IO.Put_Line ("?? Contraint_Error raised when dispatching on null.");
end;
declare
procedure Ren (X : not null access Pack.Priv) renames Pack.PPA; -- ERROR:
begin
null;
end;
end TestUgh;
----------------
GNAT 18.1 compiles and gives:
--- TestUgh - Conformance of anonymous access types with implicit null exclusions
?? Contraint_Error raised when passing null
?? Contraint_Error raised when dispatching on null
...which tells us that GNAT is essentially making the subtype null excluding
(and thus is leaking privacy). [Alternatively, it might just be implementing
the renames Legality Rules wrong.]
------
Janus/Ada 3.2.1 gives:
D:\Testing\Win\console>janus testugh.adb Janus/Ada [for Ada 95/07/12] - Version 3.2.0dev (Windows x86)
Copyright (c) 1981 - 2003, 2007, 2010 - 2016, 2017 by R.R. Software, Inc.
P.O. Box 1512, Madison WI 53701. All Rights Reserved.
Serial Number: RRS1-0000
Input File Is D:\Testing\Win\console\testugh.adb
####
Strspace ptr - 3197 Usage - 1%
Hash Table - 327 buckets used, usage - 1% Parsing Completed - 66 lines found Pass II
5 strings loaded
Open Failed
%
In File D:\Testing\Win\console\testugh.adb at line 23
--------------
22:
23: procedure PPA (X : access Priv) is -- (B)
-------------------------------^
*ERROR* Type does not match original specification (6.4.14) [RM 6.3.1(15)] Continue <Sp> or Abort <^C>?
%%
In File D:\Testing\Win\console\testugh.adb at line 35
--------------
34: begin
35: PPA (V); -- Dispatching call!
-------------------------^
*ERROR* Expression type must be specific (6.4.7) [RM 3.9.2(9)] Continue <Sp> or Abort <^C>?
In File D:\Testing\Win\console\testugh.adb at line 60
--------------
59: declare
60: procedure Ren (X : not null access Pack.Priv) renames Pack.PPA; -- ERROR:
--------------------------------------------------------------------------^
*ERROR* For parameter X
Subtype must be null excluding (6.4.7) [RM07 8.5.4(4.3/2)] Continue <Sp> or Abort <^C>?
Compiler Table Usage:
Current Max. Limit
Symbol Table 00034920H 00034920H 03274600H
Type Table 153 20000
Procedure Table 257 32000
Parse Stack 3 19 200
Compiler aborted due to Pass II errors
...that is, rejecting both the conformance and the (public) renames. It also
seems to have decided that PPA is not dispatching (which makes allowing the
body renames incorrect). Sigh.
****************************************************************
From: Randy Brukardt
Sent: Monday, August 6, 2018 8:32 PM
...
> when Constraint_Error =>
> Ada.Text_IO.Put_Line ("?? Contraint_Error raised when
> passing null");
Please, no comments on my spelling capabilities. :-)
...
> Janus/Ada 3.2.1 gives:
>
> D:\Testing\Win\console>janus testugh.adb Janus/Ada [for Ada 95/07/12]
> - Version 3.2.0dev (Windows x86)
I haven't changed the version number on the compiler yet -- but it will be
3.2.1 tomorrow. Really. :-)
****************************************************************
From: Tucker Taft
Sent: Monday, August 6, 2018 9:17 PM
> ...
> As far as the ACATS test goes, since it's not testing anything related
> to any of these clauses and it is an Ada 2005 test, I think the best
> plan is to put explicit "not null" on both the spec and the body.
> (Then it won't need modification regardless of what the result is.)
>
> Someday, we'll need an ACATS test to test that compilers actually
> implement #1, but that obviously can wait for the Ada 2020 test suite.
>
> Any objections to this plan??
Looks good to me.
****************************************************************
From: Jeff Cousins
Sent: Tuesday, August 7, 2018 4:24 PM
Sounds like a plan to me.
****************************************************************
From: Bob Duff
Sent: Thursday, August 9, 2018 6:17 AM
> For the record, given the following program:
>
> with Ada.Text_IO;
> procedure TestUgh is
> -- Tests for GNAT interpretation of AI12-0289-1 problem.
> GNAT 18.1 compiles and gives:
>
> --- TestUgh - Conformance of anonymous access types with implicit null
> exclusions ?? Contraint_Error raised when passing null ??
> Contraint_Error raised when dispatching on null
As an experiment, I modified GNAT to make this illegal (i.e. procedure PPA
does not conform to its spec).
This caused one error in PolyORB, and 10 tests failed (out of 18,000 tests
in our compiler test suite).
So people are using this feature, and making it illegal will require people to
change their code, which is currently working just fine.
I admit it's uncomfortable to have a hidden run-time check.
But it's no worse than having a formal parameter of type Natural, and assigning
it into a local variable of type Positive. It's basically just a missing
precondition, of which we have plenty others. In any case, it's not a "privacy
breakage" -- those are always compile time.
The privacy breakage is a renaming, which seems pretty obscure. IMHO
compatibility should trump minor privacy breakages. I didn't check, but I'll
bet none of the above error cases even use renamings at all.
Finally, I consider "not null" to be obsolete.
"with Predicate => Foo /= null" is better than "not null", and I'd like to
recommend it to people -- but if we require "not null" in one case, that's
uncomfortable. (I don't think we're going to say the predicate provides
conformance!)
I think this is yet another case where "required warning" or "soft error" or
whatever is the perfect solution. It tells people their code is wrong, and
they can choose whether to fix it. It nicely defuses the argument between
"but it's incompatible" and "but it's wrong".
****************************************************************
From: Tucker Taft
Sent: Thursday, August 9, 2018 6:59 AM
This is similar to an incompatibility that was discussed in AI05-0046, and
was approved. 10 tests out of 18,000 seems like a pretty small
incompatibility, and the rationale given in AI05-0046 seems to apply here as
well.
Also, "not null" is not going away, as it appears in many places in the Ada
standard run-time library.
****************************************************************
From: Tucker Taft
Sent: Thursday, August 9, 2018 8:59 AM
> I see various solutions:
> 1) Make it illegal in Ada 2005+ -- require explicit "not null" on visible
> declaration.
>
> 2) Make it illegal in Ada 2005+ -- require explicit "not null" on both
> visible and private declarations.
Based on AI05-0046, I now favor (2) over (1). AI05-0046 justifies requiring
matching(explicit) null exclusions as part of full conformance, and there
seems no reason to change that for this oddball case.
****************************************************************
From: Randy Brukardt
Sent: Thursday, August 9, 2018 3:37 PM
...
> I admit it's uncomfortable to have a hidden run-time check.
> But it's no worse than having a formal parameter of type Natural, and
> assigning it into a local variable of type Positive.
It's worse, if only because the implementation is complex. The subtype of the
specification not being null excluding means that the "hidden check" is not
going to be checked at all. And it has to be "not null excluding" so that
static matching works as expected. (This is *not* just about full conformance,
but also about any other use of static matching, i.e. in subtype conformance
and even mode conformance [the latter because mode conformance requires
static matching of anonymous access types].)
To fix that, one would have to invent a subtype that is null excluding at
runtime and not null excluding at compile-time. Possible I suppose but quite
annoying.
> It's basically just a missing precondition, of which we have plenty
> others. In any case, it's not a "privacy breakage" -- those are
> always compile time.
>
> The privacy breakage is a renaming, which seems pretty obscure. IMHO
> compatibility should trump minor privacy breakages. I didn't check,
> but I'll bet none of the above error cases even use renamings at all.
I doubt anyone uses these renamings. But in the intervening days, I've been
working on static matching and I've found it pops up in various other cases,
including matching of subprograms with formal subprograms in generic
instances, as well as 'Access.
Moreover, I recall that privacy breaking was virtually unimplementable in the
Rational Apex technology. PTC isn't represented here, so it's hard to find out
for sure, but I'd be surprised if that has changed. "Mr. Private" got that
name for a reason! He would have been very displeased about such a breakage.
So, while I can see the dynamic check being implicit, the static matching has
to be as if there is no exclusion (because there isn't!!). Is this case worth
that level of complication?
> Finally, I consider "not null" to be obsolete.
> "with Predicate => Foo /= null" is better than "not null", and I'd
> like to recommend it to people -- but if we require "not null" in one
> case, that's uncomfortable.
> (I don't think we're going to say the predicate provides
> conformance!)
This is nonsense, even if I ignore the fact that there is no such thing as
"Predicate".
(1) We're talking about an anonymous access type here, and there is no way in
Ada 2012 or planned in Ada 2020 to define a predicate for them. You're
essentially eliminating a capability. Now, if you wanted to consider
anonymous access types obsolete, I would reconsider. ;-)
(2) Why is "not null" obsolete and not "range" constraints? One can make the
same argument that a Static_Predicate is better than a range constraint.
(Although you made no such argument above, you just stated it as a fact.
No evidence to support that argument.)
(3) Predicates, like all contracts, are ignored, not suppressed. Thus, they
don't provide any information that the compiler can use when they are off,
whereas the "not null" does. (Failing a not null check is erroneous when
Suppressed.) This difference is testable and definitely is on my list of
potential ACATS tests -- "Ignore" tests are still needed in general in
the ACATS.
> I think this is yet another case where "required warning" or "soft
> error" or whatever is the perfect solution. It tells people their
> code is wrong, and they can choose whether to fix it. It nicely
> defuses the argument between "but it's incompatible" and "but it's
> wrong".
Which would double the implementation work for an obscure case. I don't feel
strongly about what decision we make, but there better be only one.
****************************************************************
From: Bob Duff
Sent: Thursday, August 9, 2018 4:25 PM
> (2) Why is "not null" obsolete and not "range" constraints?
Because scalar types don't force a default value on you, and range contraints
are not checked on uninitialized variables. That is in, "X : T range 1 .. 10;"
X might get initialized to the invalid value 999_999, but we don't check that
it's in 1 .. 10.
I'm think I've mentioned this before on this mailing list, but I could be
mistaken.
>... One can make the
> same argument that a Static_Predicate is better than a range constraint.
One can argue that range constraints are now redundant, but I don't see any
way in which one is better than the other.
****************************************************************
From: Randy Brukardt
Sent: Thursday, August 9, 2018 4:51 PM
> > (2) Why is "not null" obsolete and not "range" constraints?
>
> Because scalar types don't force a default value on you,
That comes from the language and hardly has anything to do with null
exclusions.
Personally, I think one should only have uninitialized objects by explicit
declaration (something like A : T := <>") but obviously that's not Ada
(yet :-). For most cases, they're evil (and especially since the inclusion
of conditional expressions, generally avoidable).
> ... range contraints are not checked on uninitialized variables.
> That is in, "X : T range 1 .. 10;" X might get initialized to the
> invalid value 999_999, but we don't check that it's in 1 .. 10.
Predicates aren't checked on many default-initialized objects, which is a far
worse bug IMHO. Specifically, they're not checked on any type which has
specified Default_Value (or Default_Component_Value); these are supposed to
work similarly to the := components for record types.
> I'm think I've mentioned this before on this mailing list, but I could
> be mistaken.
Not this particular issue. You insisted on a rule for predicates that I
consider broken (I haven't tried to get it fixed since it was obviously on
purpose), and that seems to be in part because of this issue. So I suppose
we've discussed it tangentially.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, September 5, 2018 4:39 PM
> > I see various solutions:
> > 1) Make it illegal in Ada 2005+ -- require explicit "not
> null" on visible declaration.
> >
> > 2) Make it illegal in Ada 2005+ -- require explicit "not
> null" on both visible and private declarations.
>
> Based on AI05-0046, I now favor (2) over (1). AI05-0046 justifies
> requiring matching(explicit) null exclusions as part of full
> conformance, and there seems no reason to change that for this oddball
> case.
Actually, the declaration of full conformance requires (2); (1) is not a
realistic option (it wouldn't make sense to weaken conformance for this
particular case).
The important issue here is that these two types fail to statically match
(and keep in mind that anonymous types are always different with each
occurrence in the source code), and that has consequences for any place that
is required, including mode and subtype conformance.
****************************************************************
Questions? Ask the ACAA Technical Agent