Version 1.4 of ai12s/ai12-0301-1.txt

Unformatted version of ai12s/ai12-0301-1.txt version 1.4
Other versions for file ai12s/ai12-0301-1.txt

!standard 3.2.4(31/4)          19-03-04 AI12-0301-1/03
!class binding interpretation 19-01-09
!status Amendment 1-2012 19-01-15
!status WG9 Approved 22-06-22
!status ARG Approved 11-0-0 19-01-14
!status work item 19-01-09
!status received 18-12-17
!priority Low
!difficulty Easy
!qualifier Omission
!subject Predicates should be checked like constraints for types with Default_Value
!summary
If a part of a default-initialized object has specified Default_Value or Default_Component_Value aspects, predicates are evaluated for the object.
!question
3.2.4(31/4) says that predicates are not checked for default-initialized objects unless "any subcomponents have default_expressions". This seems different than the rule for constraints and exclusions of stand-alone objects, which check those if an implicit initial value is defined.
Should this be fixed? (Yes.)
!recommendation
(See Summary.)
!wording
Modify 3.2.4(31/4):
Redundant[On every subtype conversion, a check is performed that the operand satisfies the predicates of the target subtype. This includes all parameter passing, except for certain parameters passed by reference, which are covered by the following rule: ] After normal completion and leaving of a subprogram, for each /in out/ or /out/ parameter that is passed by reference, a check is performed that the value of the parameter satisfies the predicates of the subtype of the actual. For an object created by an object_declaration with no explicit initialization expression, or by an uninitialized allocator, if {the types of any parts have specified Default_Value or Default_Component_Value aspects, or} any subcomponents have default_expressions, a check is performed that the value of the created object satisfies the predicates of the nominal subtype.
[Author's note: We have to talk about "parts" in the new text as we need to cover the case where the type of the object itself has one of these aspects. OTOH, the existing wording only applies to subcomponents, as there is no such thing as a default expression for a type (that is the role of these aspects.]
!discussion
A default-initialized stand-alone object of a type with Default_Value has the constraint checked on the value (that occurs when the value is converted to the nominal subtype, see 3.3.1(11)). The current rule for predicates do not check the predicates.
As much as possible, we want the rules for constraints and predicates to be the same, so that changing between them can be seamless. Minor differences are likely to be confusing.
The predicate rule was defined this way in part so that default initialization of access values would not trigger predicate evaluation. That is to say, the difference between the behavior of the "not null" exclusion and predicates is intentional (at least by the author of the rules). On the other hand, leaving out Default_Value and Default_Component_Value was unintentional.
The easiest rule would be make the rules exactly consistent, by checking the predicate on any type which has a part that has an implicit initial value. However, that would change the clear intent of the rule. Moreover, access types are common and it is likely that at least some programs depend on the predicate checks not being made.
So we adopt a correction only for types with parts that have Default_Value or Default_Component_Value aspects. We justify this change in part by saying that the programmer has requested a default value and thus that value should be enforced like any other. The issue with access types is that there is no way to not have a default value, so one is provided whether it makes sense for a particular type or not.
There is a small compatibility issue with this change, in that some objects will get predicate checks that did not in the past. This is not as likely to happen as Default_Value is used much less frequently than access types, and it was a new feature in Ada 2012, so it does not appear in any legacy code.
!corrigendum 3.2.4(31/4)
Replace the paragraph:
On every subtype conversion, a check is performed that the operand satisfies the predicates of the target subtype. This includes all parameter passing, except for certain parameters passed by reference, which are covered by the following rule: After normal completion and leaving of a subprogram, for each in out or out parameter that is passed by reference, a check is performed that the value of the parameter satisfies the predicates of the subtype of the actual. For an object created by an object_declaration with no explicit initialization expression, or by an uninitialized allocator, if any subcomponents have default_expressions, a check is performed that the value of the created object satisfies the predicates of the nominal subtype.
by:
On every subtype conversion, a check is performed that the operand satisfies the predicates of the target subtype. This includes all parameter passing, except for certain parameters passed by reference, which are covered by the following rule: After normal completion and leaving of a subprogram, for each in out or out parameter that is passed by reference, a check is performed that the value of the parameter satisfies the predicates of the subtype of the actual. For an object created by an object_declaration with no explicit initialization expression, or by an uninitialized allocator, if the types of any parts have specified Default_Value or Default_Component_Value aspects, or any subcomponents have default_expressions, a check is performed that the value of the created object satisfies the predicates of the nominal subtype.
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-test needs to be constructed to ensure the checks are made in this case.
!appendix

From: Tucker Taft
Sent: Monday, December 17, 2018  3:43 PM

It seems a bit weird to me that a composite object without an explicit 
initialization has its subtype predicates checked, if any subcomponent has 
default initialization, while an elementary type with a default 
initialization doesn't have any subtype predicate check on a default 
initialization.

Last sentence of RM 3.2.4(31/4) says:

"... For an object created by an object_declaration with no explicit 
initialization expression, or by an uninitialized allocator, if any 
subcomponents have default_expressions, a check is performed that the value 
of the created object satisfies the predicates of the nominal subtype."

Do others find this strange as well?  If this said "..., if any *part has 
default initialization,* a check is performed ..." would make more sense to 
me.

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

From: Gary Dismukes
Sent: Monday, December 17, 2018  4:05 PM

> Do others find this strange as well?  If this said "..., if any *part has
> default initialization,* a check is performed ..." would make more sense 
> to me.

I agree that seems odd and would expect them to be treated the same way.

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

From: Randy Brukardt
Sent: Monday, December 17, 2018  4:12 PM

> Do others find this strange as well?  If this said "..., if any *part 
> has default initialization,* a check is performed ..." would make more 
> sense to me.

Yes, I've complained about this repeatedly.

This would also have the pleasant side-effect of making null exclusions and 
dynamic predicates work the same way.

Bob, however, seems to have a hidden agenda to allow incorrectly initialized 
objects (I'm not sure why -- to me, the behavior of uninitialized constrained
scalar objects is a language bug that we have to live with, not a useful 
feature). So I think he'll resist.

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

From: Bob Duff
Sent: Tuesday, December 18, 2018  1:24 PM

> Do others find this strange as well?  If this said "..., if any *part 
> has default initialization,* a check is performed ..." would make more 
> sense to me.

I agree.

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

From: Tucker Taft
Sent: Tuesday, December 18, 2018  1:49 PM

Bob didn't resist.  But I am not sure my suggestion was completely clear.
In particular, it would only affect elementary subtypes with both a 
subtype predicate, and some default initialization specified (i.e. an 
access subtype or a subtype of a type that has Default_Value => ...). 
In that special case, an object of such a subtype when created without
an explicit initial value, would nevertheless have its subtype predicate
checked after performing default initialization.

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

From: Bob Duff
Sent: Tuesday, December 18, 2018  2:12 PM

> Bob didn't resist.

Because I thought you were talking about "Default_Value =>"
for scalars.

> ...But I am not sure my suggestion was completely clear.  In 
> particular, it would only affect elementary subtypes with both a 
> subtype predicate, and some default initialization specified (i.e. an 
> access subtype or a subtype of a type that has Default_Value => ...). 
> In that special case, an object of such a subtype when created without 
> an explicit initial value, would nevertheless have its subtype 
> predicate checked after performing default initialization.

Now that I see you are talking about access as well, I object.
The decision that implicit init to null should NOT trigger the predicate check 
was deliberate, and would be incompatible.
Don't you think it's too late to make that change (only for access)?  I think 
I've written code that will break, like this:

    X : Some_Record_Containing_Access_Components;
    ...
    X := ...;

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

From: Tucker Taft
Sent: Tuesday, December 18, 2018  2:25 PM

> Now that I see you are talking about access as well, I object.
> The decision that implicit init to null should NOT trigger the 
> predicate check was deliberate, and would be incompatible.

Both of these ideas are incompatible.  I had forgotten the underlying 
reasoning related to the access type case.

> Don't you think it's too late to make that change (only for access)?

It just seemed inconsistent.  I wanted to see if there was sympathy with 
"fixing" this inconsistency.  Apparently the access type "inconsistency" was
intentional, while perhaps the scalar-type-with-default-value inconsistency 
was unintentional. 

>  I think I've written code that will break, like this:
> 
>    X : Some_Record_Containing_Access_Components;
>    ...
>    X := ...;

Subtype predicates are already being checked on such a record type.  So your 
concern must be about subtype predicates on the access subtypes used for the 
components of the record, which will fail if they are null.  Is that the 
example you had in mind?

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

From: Bob Duff
Sent: Tuesday, December 18, 2018  3:42 PM

> Both of these ideas are incompatible.

Yes.  But I'd think the Default_Value would typically obey the predicate, 
where 'null' probably does not.

> ...I had forgotten the underlying reasoning related to the access type 
> case.

Right, the problem is that you get implicit initialization whether you want 
it or not.  That's not true of anything other than access types.

It would be better to init access values to something that blows up if you 
look at it in any way, not just if you dereference it.  Too late for that.

> > Don't you think it's too late to make that change (only for access)?
> 
> It just seemed inconsistent.  I wanted to see if there was sympathy 
> with "fixing" this inconsistency.  Apparently the access type 
> "inconsistency" was intentional, while perhaps the 
> scalar-type-with-default-value inconsistency was unintentional.

I think the Default_Value case was a mistake, and I think it was my fault.

> >  I think I've written code that will break, like this:
> > 
> >    X : Some_Record_Containing_Access_Components;
> >    ...
> >    X := ...;
> 
> Subtype predicates are already being checked on such a record type.
> So your concern must be about subtype predicates on the access 
> subtypes used for the components of the record, which will fail if 
> they are null.  Is that the example you had in mind?

My example was confusing.  Sorry.

There is no predicate check on the decl of X, and you were proposing to change 
that, and I object.

There is a predicate check when you assign into X, and that's a good thing 
(I'm sure we both agree).

You can say:

    X : Scalar_Type_With_Predicate;
    ...
    X := ...;

and it works fine.  Same for a record containing scalars (without defaults).  
I just want to be able to do the same thing for access types, and for 
composites containing access types.

Note that predicates work differently from "not null" in this regard, and last 
time I pointed that out, you wanted to change "not null" to work like 
predicates (i.e. going in the opposite direction as what you suggested in this 
email thread). I didn't want to change "not null", because I think it's an 
obsolete feature -- use predicates instead.  But I don't STRONGLY object to 
that change -- it just seems like a waste of time.

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

From: Tucker Taft
Sent: Tuesday, December 18, 2018  4:00 PM

> ...
> 
> Note that predicates work differently from "not null" in this regard, 
> and last time I pointed that out, you wanted to change "not null" to 
> work like predicates (i.e. going in the opposite direction as what you 
> suggested in this email thread).

Unfortunately, I don't have any specific memory of this or what made me feel 
that way.

It does seem way too late to change the behavior of "not null," and these days 
having uninitialized variables seems less and less necessary, given all of the 
ways to create values that incorporate conditionality.

> I didn't want to change "not null", because I think it's an obsolete 
> feature -- use predicates instead.  But I don't STRONGLY object to 
> that change -- it just seems like a waste of time.

It seems too late to me now to change the behavior of "not null."  I still 
don't like the inconsistency between Default_Value'd scalar types and 
composites with defaulted components, but the whole thing seems like a bit of 
a morass now.  If everyone agreed scalar types with Default_Value specified 
should be treated the same as composite types with some defaults somewhere, 
then I would support that.  But it doesn't really resolve the overall 
inconsistency, so not sure it is worth it.

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

From: Bob Duff
Sent: Tuesday, December 18, 2018  4:13 PM

> It seems too late to me now to change the behavior of "not null."  I 
> still don't like the inconsistency between Default_Value'd scalar 
> types and composites with defaulted components, but the whole thing 
> seems like a bit of a morass now.  If everyone agreed scalar types 
> with Default_Value specified should be treated the same as composite 
> types with some defaults somewhere, then I would support that.

I think I support that.

>...But it
> doesn't really resolve the overall inconsistency, so not sure it is  
>worth it.

I can make it sound sort-of consistent:  If the programmer explicitly asked 
for defaults, they get a predicate check on an object decl with no init.  
If they didn't, then they don't.

It kind of makes sense -- if you asked for defaults, you presumably make the 
default obey the predicate, except in a case like this:

   type Constant_Reference_Type
     (Element : not null access constant Element_Type) is
      record
         Control : Reference_Control_Type :=
           raise Program_Error with "uninitialized reference";
         --  The RM says, "The default initialization of an object of
         --  type Constant_Reference_Type or Reference_Type propagates
         --  Program_Error."
      end record;

If the language forces you to have a particular default, you can't make it 
obey the predicate without loosening the predicate.

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

From: Randy Brukardt
Sent: Tuesday, December 18, 2018  8:06 PM

> > Both of these ideas are incompatible.
> 
> Yes.  But I'd think the Default_Value would typically obey the 
> predicate, where 'null' probably does not.
> 
> > ...I had forgotten the underlying reasoning related to the
> access type
> > case.
> 
> Right, the problem is that you get implicit initialization whether you 
> want it or not.  That's not true of anything other than access types.

That's not really accurate: you get implicit initialization of discriminants 
whether or not you initialize anything else in a record. For an unconstrained,
mutable type, that can be an issue. You also have such a requirement for a 
tagged type (the tag being initialized).

But I agree with Tucker: there is very little reason anymore to use any 
uninitialized objects. It's highly unfortunate that the easy thing to declare 
is an uninitialized variable, while what you ought to try to declare is an 
initialized constant. But of course, what made sense in 1978 doesn't make as
much sense now. (Definitely something I would change in my "better Ada".)

The only reason I know of is when you have a massive array covering almost all 
of memory that you will use an unknown part of. (That happened in my website 
log analyzer.) But in that case, it is the discriminant and/or tag 
initialization that kills you (such things tend to be some sort of complex 
record type). Access initialization might have been a problem, but the 
discriminant already forced it.

> It would be better to init access values to something that blows up if 
> you look at it in any way, not just if you dereference it.  Too late 
> for that.

True, but this is just another thing that Ada 83 got wrong that we're stuck 
with. And the entire notion of objects that are invalid *after* declaration is 
bogus (but necessary for compatibility with Ada 83). IMHO, the only objects 
that should be allowed to be uninitialized are those with no constraints at 
all.
 
> > > Don't you think it's too late to make that change (only for access)?
> > 
> > It just seemed inconsistent.  I wanted to see if there was sympathy 
> > with "fixing" this inconsistency.  Apparently the access type 
> > "inconsistency" was intentional, while perhaps the 
> > scalar-type-with-default-value inconsistency was unintentional.
> 
> I think the Default_Value case was a mistake, and I think it was my 
> fault.

BTW, we're also talking about the Default_Component_Value case, but I don't 
think anyone is worried about treating those differently.

> > >  I think I've written code that will break, like this:
> > > 
> > >    X : Some_Record_Containing_Access_Components;
> > >    ...
> > >    X := ...;
> > 
> > Subtype predicates are already being checked on such a record type.
> > So your concern must be about subtype predicates on the access 
> > subtypes used for the components of the record, which will fail if 
> > they are null.  Is that the example you had in mind?
> 
> My example was confusing.  Sorry.
> 
> There is no predicate check on the decl of X, and you were proposing 
> to change that, and I object.

Which is what I told Tucker in his original message.
 
> There is a predicate check when you assign into X, and that's a good 
> thing (I'm sure we both agree).
> 
> You can say:
> 
>     X : Scalar_Type_With_Predicate;
>     ...
>     X := ...;
> 
> and it works fine.

Yes, and that's so it is consistent with:

    X : Scalar_Type range ...;

But the above is a rather nasty misfeature of Ada, and I've always objected 
to extending that misfeature further. The only objects that should be 
allowed to be uninitialized are those with no constraints (or those that match 
the constraints of 'Base).

> Same for a record containing scalars
> (without defaults).  I just want to be able to do the same thing for 
> access types, and for composites containing access types.

Understood, but I see no good reason that you should be allowed to. Ada 83 
botched the definition of scalar types, and we're stuck with that. But why 
extend the misfeature even further?

I almost always explicitly initialize null access objects (so I can read what is 
happening anyway, and so I know that I properly considered what to initialize 
the objects to) -- but in your world that does something different. I find 
that distasteful.
 
> Note that predicates work differently from "not null" in this regard, 
> and last time I pointed that out, you wanted to change "not null" to 
> work like predicates (i.e. going in the opposite direction as what you 
> suggested in this email thread).
> I didn't want to change "not null", because I think it's an obsolete 
> feature -- use predicates instead.  But I don't STRONGLY object to 
> that change -- it just seems like a waste of time.
 
I would strongly object to changing "not null", because it works the way the 
entire language should. (And we discussed this once before, but "not null"
is not obsolete in any way -- it provides an assertion that the compiler can 
enforce and more importantly trust. To do that with predicates requires flow 
analysis [in Janus/Ada at least, that comes too late to be useful in checking
elimination]. [On top of which, that other argument was about anonymous access
types, which can't have predicates in the first place, so how "not null" can 
be obsolete for them escapes me. :-)])

And I don't see much reason to make a halfway change to predicates. Any change 
would be incompatible at this point, but such a change would mostly force 
people to write better code. So I personally would be in favor of such a 
change. But I don't see any reason to change the rule just to leave a "Bob"
hole in it. At least the current rule has some form of logic.

P.S. I think we actually argued all of this before approving the predicate AI 
in the first place. The current rule was a compromise that no one really 
likes. It is precisely why predicate use is less useful than it appears -- and 
unlikely type invariants, it didn't have to be that way. Grumble.

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

From: Randy Brukardt
Sent: Tuesday, December 18, 2018  9:05 PM

...
> Note that predicates work differently from "not null" in this regard, 
> and last time I pointed that out, you wanted to change "not null" to 
> work like predicates (i.e. going in the opposite direction as what you 
> suggested in this email thread).

["you" here meaning Tucker.]

I don't recall any such discussion on ARG or Ada-Comment, nor any position 
from Tucker on this issue. The only discussion that happened here that I 
remember was about "not null" and anonymous access types. I'd guess you are
remembering this incorrectly. (Of course, there's always a chance that it 
happened somewhere else, and Tucker was on his third glass of wine at the 
time. ;-)

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

From: Bob Duff
Sent: Wednesday, December 19, 2018  8:46 AM

> I don't recall any such discussion on ARG or Ada-Comment, nor any 
> position from Tucker on this issue.

I thought it was on arg@, but if you don't remember it, maybe it was within 
AdaCore.  I don't think I'm imagining things (but I could be wrong).

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

From: Randy Brukardt
Sent: Thursday, January 3, 2019  10:06 PM

...
 It seems too late to me now to change the behavior of "not 
> null."  I still don't like the inconsistency between Default_Value'd 
> scalar types and composites with defaulted components, but the whole 
> thing seems like a bit of a morass now.  If everyone agreed scalar 
> types with Default_Value specified should be treated the same as 
> composite types with some defaults somewhere, then I would support 
> that.  But it doesn't really resolve the overall inconsistency, so not 
> sure it is worth it.

I certainly agree; it would be more consistent with the rest of the language
(where the presence of Default_Value changes the semantics).

Bob replied:
> I can make it sound sort-of consistent:  If the programmer explicitly 
> asked for defaults, they get a predicate check on an object decl with 
> no init.  If they didn't, then they don't.

And the reason for replying to this now: I'd guess that we'd want to include 
Default_Component_Value in any rule change as well. A component with such a 
type also is default-initialized by something under control of the user.

>It kind of makes sense -- if you asked for defaults, you presumably 
>make the default obey the predicate, except in a case like this:
>
>   type Constant_Reference_Type
>     (Element : not null access constant Element_Type) is
>      record
>         Control : Reference_Control_Type :=
>           raise Program_Error with "uninitialized reference";
>         --  The RM says, "The default initialization of an object of
>         --  type Constant_Reference_Type or Reference_Type propagates
>         --  Program_Error."
>      end record;
>
>If the language forces you to have a particular default, you can't make 
>it obey the predicate without loosening the predicate.

Well, in this case, it doesn't matter what the predicate is 'cause the default
initialization will raise an exception before any predicate could be 
evaluated. The point was of course to prevent default-initialized objects from 
existing. That would have been a better way to handle access types, but like 
everything it is way too late.

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

From: Tucker Taft
Sent: Friday, January 4, 2019  8:19 AM

> ...
> It seems too late to me now to change the behavior of "not
>> null."  I still don't like the inconsistency between Default_Value'd 
>> scalar types and composites with defaulted components, but the whole 
>> thing seems like a bit of a morass now.  If everyone agreed scalar 
>> types with Default_Value specified should be treated the same as 
>> composite types with some defaults somewhere, then I would support 
>> that.  But it doesn't really resolve the overall inconsistency, so 
>> not sure it is worth it.
> 
> I certainly agree; it would be more consistent with the rest of the 
> language (where the presence of Default_Value changes the semantics).

When you say "it would be more consistent," what does "it" refer to?
 
> Bob replied:
>> I can make it sound sort-of consistent:  If the programmer explicitly 
>> asked for defaults, they get a predicate check on an object decl with 
>> no init.  If they didn't, then they don't.
> 
> And the reason for replying to this now: I'd guess that we'd want to 
> include Default_Component_Value in any rule change as well. A 
> component with such a type also is default-initialized by something under 
> control of the user.

Agreed.  To be consistent, specifying Default_Component_Value should trigger 
evaluation of any predicates, whether on the component subtype or the array 
subtype.
 
>> It kind of makes sense -- if you asked for defaults, you presumably 
>> make the default obey the predicate, except in a case like this:
>> 
>>  type Constant_Reference_Type
>>    (Element : not null access constant Element_Type) is
>>     record
>>        Control : Reference_Control_Type :=
>>          raise Program_Error with "uninitialized reference";
>>        --  The RM says, "The default initialization of an object of
>>        --  type Constant_Reference_Type or Reference_Type propagates
>>        --  Program_Error."
>>     end record;
>> 
>> If the language forces you to have a particular default, you can't 
>> make it obey the predicate without loosening the predicate.

I suppose.  As far as whether to use copy-in for "out" parameters, we don't 
make such a distinction between Default-Value'd scalars and access types.  On
the other hand, for "out" parameters, we never check the access subtype on the 
copied-in value (RM 6.4.1(13/3)), so that does represent somewhat of a special 
case for access types.  

On the other, other hand, scalars with Default_Value and composite types with
some default-initialized components are treated differently for OUT parameters
as far as checking predicates.  For a scalar OUT parameter, we never check the
predicate even if we copy in, whereas for composite OUT parameters with any 
defaulted components, they are treated like IN OUT parameters, and hence will 
have predicates checked.  So OUT parameters have somewhat the same 
inconsistency we were worried about between Default_Value'd scalars and 
composites with default-init'ed components.

So in as much as we see OUT parameters and not-explicitly-initialized objects 
as being similar, the same sort of inconsistencies exist in both cases.
 
> Well, in this case, it doesn't matter what the predicate is 'cause the 
> default initialization will raise an exception before any predicate 
> could be evaluated. The point was of course to prevent 
> default-initialized objects from existing. That would have been a 
> better way to handle access types, but like everything it is way too late.

Not sure what you mean by this last sentence.  What would have been a better 
way to handle access types?

Bottom line for me:  The rules for whether to check predicates on 
non-explicitly-initialized objects are not sufficiently broken, and are too 
hard to decide on any possible fix.

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

From: Randy Brukardt
Sent: Friday, January 4, 2019  4:54 PM

...
> > I certainly agree; it would be more consistent with the rest of the 
> > language (where the presence of Default_Value changes the
> semantics).
> 
> When you say "it would be more consistent," what does "it" refer to?

Changing the predicate rule to include components with Default_Value or 
Default_Component_Value. Bob's justification is a bit hand wavy, but it's at 
least understandable.

Personally, I'd rather include any default-initialized object (that is, 
including access values) -- I don't see any good reason to make a language 
mistake worse. (That is, that there are cases where the constraint/predicate 
of an object is allowed to be False. We obviously were stuck with that from 
Ada 83, but that doesn't make it any better.)

But clearly Bob would object to that, he has a different view and he's not 
likely to be swayed even now. (I'm pretty sure that the design of "not null"
would have been different had he been participating regularly in the ARG work
when that was defined. All of the Legality Rules about "not null" exist so that
there never is a case where a "not null" object contains null -- the feeling at
the time was that is how Ada should have worked, but we were stuck with a bad 
definition. I'm sure that group would have made predicates work like "not 
null".) And he's right that there would be a compatibility issue, which is 
less concerning for Default_Value/Default_Component_Value,
which are much less used than access types.

...
> I suppose.  As far as whether to use copy-in for "out" 
> parameters, we don't make such a distinction between Default-Value'd 
> scalars and access types.  On the other hand, for "out" parameters, we 
> never check the access subtype on the copied-in value (RM 
> 6.4.1(13/3)), so that does represent somewhat of a special case for 
> access types.
> 
> On the other, other hand, scalars with Default_Value and composite 
> types with some default-initialized components are treated differently 
> for OUT parameters as far as checking predicates.  For a scalar OUT 
> parameter, we never check the predicate even if we copy in, whereas 
> for composite OUT parameters with any defaulted components, they are 
> treated like IN OUT parameters, and hence will have predicates 
> checked.  So OUT parameters have somewhat the same inconsistency we 
> were worried about between Default_Value'd scalars and composites with 
> default-init'ed components.
> 
> So in as much as we see OUT parameters and not-explicitly-initialized 
> objects as being similar, the same sort of inconsistencies exist in 
> both cases.

I originally started to write something about OUT parameters in my reply, but 
after reading the rules I couldn't decide if it proved anything, so I deleted 
that paragraph. And I think you see why. ;-)

> > Well, in this case, it doesn't matter what the predicate is 'cause 
> > the default initialization will raise an exception before any 
> > predicate could be evaluated. The point was of course to prevent 
> > default-initialized objects from existing. That would have been a 
> > better way to handle access types, but like everything it is way too 
> > late.
> 
> Not sure what you mean by this last sentence.  What would have been a 
> better way to handle access types?

I was thinking out loud. Ada 83 was trying to prevent the existence of 
uninitialized access values. It adopted a rule forcibly initializing them to
null. It would have been better to simply have them raise an exception like 
Program_Error (meaning that all such objects would have to be initialized).
It's not that much harder to write:

    Temp : My_Access := null;

than

    Temp : My_Access;

and it's way clearer. (Indeed, default-initialization ought to be rare in 
newly written Ada 2012 and later code; I'd prefer that it be marked if you 
want that, something like:

    Temp : Natural := <>;

rather than having the easy thing to write be the least safe and most 
expensive in use. But that will have to wait for "Better Ada".)

The alternative of default-initializing access values to "bottom", which raise
an exception when read, is too expensive for Ada (as it implies overhead on 
most reads - that overhead could be eliminated with a mechanism similar to the
one used by Janus/Ada for validity - if the object is explicitly initialized, 
no reading checks are needed - even so, that seems like a bridge too far).

> Bottom line for me:  The rules for whether to check predicates on 
> non-explicitly-initialized objects are not sufficiently broken, and 
> are too hard to decide on any possible fix.

I think that rule is severely broken, and I knew that when it was written.
(I probably should have fought harder at the time, but one has to pick his
battles...) Bob being the reason that it is broken, and him having agreed to
a small fix which improves the situation, seems to me to be enough reason to 
formally propose a fix along the lines of what Bob has said he'd agree to.
(As I noted above, I have to be sensitive to the likelihood that a full fix 
would be too incompatible to adopt.)

P.S. This topic always seems to get me worked up and I write far too much on 
it. Sorry. 

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

From: Tucker Taft
Sent: Friday, January 4, 2019  5:27 PM

> I was thinking out loud. Ada 83 was trying to prevent the existence of
> uninitialized access values. It adopted a rule forcibly initializing them to
> null. It would have been better to simply have them raise an exception like
> Program_Error (meaning that all such objects would have to be initialized).
> It's not that much harder to write:
>
>   Temp : My_Access := null;
>
> than
>
>   Temp : My_Access;
>
> and it's way clearer. (Indeed, default-initialization ought to be rare in
> newly written Ada 2012 and later code; I'd prefer that it be marked if you
> want that, something like:
>
>   Temp : Natural := <>;
>
> rather than having the easy thing to write be the least safe and most
> expensive in use. But that will have to wait for "Better Ada".)

When doing static analysis, it is tricky to distinguish between the programmer 
omitting the explicit initialization specifically to get the default, or 
omitting it because it must be initialized explicitly later.  Some languages
happily default initialize everything to something like zero, which 
exacerbates this problem.

I agree with you that something like "<>" might have been a better way to 
explicitly get some "default" value.  The new "[]" notation may emerge as 
something approximating that for containers.  In ParaSail, "()" can be used 
when all components of a (visible) type have defaults.  And finally there is 
"null" in ParaSail, which can be used with any type, representing the absence 
of a value.

Isn't language design fun? ;-)

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

From: Randy Brukardt
Sent: Friday, January 4, 2019  5:54 PM

>When doing static analysis, it is tricky to distinguish between the programmer 
>omitting the explicit initialization specifically to get the default, 
>or omitting it because it must be initialized explicitly later.  Some languages 
>happily default initialize everything to something like zero, which exacerbates
>this problem.

And of course "reading code" is just static analysis performed by the 
programmer rather than a machine. If it's hard to statically analyze, it's 
probably a readability problem too.

>I agree with you that something like "<>" might have been a better way 
>to explicitly get some "default" value.  The new "[]" notation may 
>emerge as something approximating that for containers.  In ParaSail, 
>"()" can be used when all components of a (visible) type have defaults.  
>And finally there is "null" in ParaSail, which can be used with any 
>type, representing the absence of a value.

You might remember that Ada 2005 briefly had "(<>)" as the default-initialized 
object of any composite type. As I recall, there were some semantic issues with
that that got it tossed out at the last minute (during the snowstorm in Paris).
It probably could have been made to work, but it didn't seem important enough 
at the time. I've rather regretted that, as I wanted to use it and a 
restriction (No_Uninitialized_Objects) to at least allow/enforce a better 
style.

>Isn't language design fun? ;-)

Surely. I've spent way too much time thinking about how best to handle arrays 
and strings in "Better Ada". One of these days I'll write something up (as a 
blog entry, not here).

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

Questions? Ask the ACAA Technical Agent