comp.lang.ada
 help / color / mirror / Atom feed
* Unexpected discriminant check failure involving access types
@ 2015-08-10 12:38 Markus Schöpflin
  2015-08-10 13:14 ` Mark Lorenzen
  2015-08-10 14:33 ` Niklas Holsti
  0 siblings, 2 replies; 7+ messages in thread
From: Markus Schöpflin @ 2015-08-10 12:38 UTC (permalink / raw)


Given the following piece of code:

---%<---
      1  procedure TEST
      2  is
      3     type T is (T1, T2);
      4
      5     type RECORD_T (X : T := T1) is record
      6        null;
      7     end record;
      8
      9     type PTR_T is access RECORD_T;
     10
     11     FOO : RECORD_T;
     12     FOO_PTR : constant PTR_T := new RECORD_T;
     13
     14     FOO1 : constant RECORD_T := (X => T1);
     15     FOO2 : constant RECORD_T := (X => T2);
     16  begin
     17     FOO := FOO1;
     18     FOO := FOO2;
     19
     20     FOO_PTR.all := FOO1;
     21     FOO_PTR.all := FOO2;
     22  end;
--->%---

When compiled and executed, I get:

 > ./test

raised CONSTRAINT_ERROR : test.adb:21 discriminant check failed

Can anyone please explain me why I get a discriminant check error when using 
access types? I would have expected it to work the same as for the non-access 
case.

TIA, Markus


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Unexpected discriminant check failure involving access types
  2015-08-10 12:38 Unexpected discriminant check failure involving access types Markus Schöpflin
@ 2015-08-10 13:14 ` Mark Lorenzen
  2015-08-10 14:20   ` Markus Schöpflin
  2015-08-10 14:33 ` Niklas Holsti
  1 sibling, 1 reply; 7+ messages in thread
From: Mark Lorenzen @ 2015-08-10 13:14 UTC (permalink / raw)


On Monday, August 10, 2015 at 2:38:23 PM UTC+2, Markus Schöpflin wrote:
> Given the following piece of code:
> 
> ---%<---
>       1  procedure TEST
>       2  is
>       3     type T is (T1, T2);
>       4
>       5     type RECORD_T (X : T := T1) is record
>       6        null;
>       7     end record;
>       8
>       9     type PTR_T is access RECORD_T;
>      10
>      11     FOO : RECORD_T;
>      12     FOO_PTR : constant PTR_T := new RECORD_T;
>      13
>      14     FOO1 : constant RECORD_T := (X => T1);
>      15     FOO2 : constant RECORD_T := (X => T2);
>      16  begin
>      17     FOO := FOO1;
>      18     FOO := FOO2;
>      19
>      20     FOO_PTR.all := FOO1;
>      21     FOO_PTR.all := FOO2;
>      22  end;
> --->%---
> 
> When compiled and executed, I get:
> 
>  > ./test
> 
> raised CONSTRAINT_ERROR : test.adb:21 discriminant check failed
> 
> Can anyone please explain me why I get a discriminant check error when using 
> access types? I would have expected it to work the same as for the non-access 
> case.
> 
> TIA, Markus

Look at line 12. FOO_PTR is a constant object of an access type. The constant object is initialized to point to a discriminated record with discriminant T1, since this is the default discriminant of objects of type RECORD_T. In line 21 it goes horribly wrong and you try to change the object pointed to, to a record with discriminant T2.

What problem are you trying to solve? Is the above code just a simple reproducer or is it a snippet from real code?

Regards,

Mark L

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Unexpected discriminant check failure involving access types
  2015-08-10 13:14 ` Mark Lorenzen
@ 2015-08-10 14:20   ` Markus Schöpflin
  2015-08-10 19:00     ` Randy Brukardt
  0 siblings, 1 reply; 7+ messages in thread
From: Markus Schöpflin @ 2015-08-10 14:20 UTC (permalink / raw)


Am 10.08.2015 um 15:14 schrieb Mark Lorenzen:
> On Monday, August 10, 2015 at 2:38:23 PM UTC+2, Markus Schöpflin wrote:
>> Given the following piece of code:
>>
>> ---%<---
>>        1  procedure TEST
>>        2  is
>>        3     type T is (T1, T2);
>>        4
>>        5     type RECORD_T (X : T := T1) is record
>>        6        null;
>>        7     end record;
>>        8
>>        9     type PTR_T is access RECORD_T;
>>       10
>>       11     FOO : RECORD_T;
>>       12     FOO_PTR : constant PTR_T := new RECORD_T;
>>       13
>>       14     FOO1 : constant RECORD_T := (X => T1);
>>       15     FOO2 : constant RECORD_T := (X => T2);
>>       16  begin
>>       17     FOO := FOO1;
>>       18     FOO := FOO2;
>>       19
>>       20     FOO_PTR.all := FOO1;
>>       21     FOO_PTR.all := FOO2;
>>       22  end;
>> --->%---
>>
>> When compiled and executed, I get:
>>
>>   > ./test
>>
>> raised CONSTRAINT_ERROR : test.adb:21 discriminant check failed
>>
>> Can anyone please explain me why I get a discriminant check error when using
>> access types? I would have expected it to work the same as for the non-access
>> case.
>>
>> TIA, Markus
>
> Look at line 12. FOO_PTR is a constant object of an access type. The
> constant object is initialized to point to a discriminated record with
> discriminant T1, since this is the default discriminant of objects of type
> RECORD_T. In line 21 it goes horribly wrong and you try to change the
> object pointed to, to a record with discriminant T2.

I still don't get it: I would have expected the objects FOO and
FOO_PTR.all to be identical in behaviour. Why is "new RECORD_T" creating a
constrained record and just using "RECORD_T" not?

> What problem are you trying to solve? Is the above code just a simple
> reproducer or is it a snippet from real code?

That is a reproducer for a bug found in some real code, which suddenly started 
to give constraint errors, due to a change in the initialization of a variable 
of type PTR_T.

Markus


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Unexpected discriminant check failure involving access types
  2015-08-10 12:38 Unexpected discriminant check failure involving access types Markus Schöpflin
  2015-08-10 13:14 ` Mark Lorenzen
@ 2015-08-10 14:33 ` Niklas Holsti
  2015-08-10 14:56   ` Markus Schöpflin
  1 sibling, 1 reply; 7+ messages in thread
From: Niklas Holsti @ 2015-08-10 14:33 UTC (permalink / raw)


On 15-08-10 15:38 , Markus Schöpflin wrote:
> Given the following piece of code:
>
> ---%<---
>       1  procedure TEST
>       2  is
>       3     type T is (T1, T2);
>       4
>       5     type RECORD_T (X : T := T1) is record
>       6        null;
>       7     end record;
>       8
>       9     type PTR_T is access RECORD_T;
>      10
>      11     FOO : RECORD_T;
>      12     FOO_PTR : constant PTR_T := new RECORD_T;
>      13
>      14     FOO1 : constant RECORD_T := (X => T1);
>      15     FOO2 : constant RECORD_T := (X => T2);
>      16  begin
>      17     FOO := FOO1;
>      18     FOO := FOO2;
>      19
>      20     FOO_PTR.all := FOO1;
>      21     FOO_PTR.all := FOO2;
>      22  end;
> --->%---
>
> When compiled and executed, I get:
>
>  > ./test
>
> raised CONSTRAINT_ERROR : test.adb:21 discriminant check failed
>
> Can anyone please explain me why I get a discriminant check error when
> using access types? I would have expected it to work the same as for the
> non-access case.

This is a consequence of RM 4.8(6/3): "...  If the designated type is 
composite, then the subtype of the created object is the designated 
subtype when the designated subtype is constrained or there is an 
ancestor of the designated type that has a constrained partial view; 
otherwise, the created object is constrained by its initial value (even 
if the designated subtype is unconstrained with defaults)."

In other words, the object FOO_PTR.all is constrained and it is not 
possible to change its discriminants -- the default value given to the 
discriminant X, which means that FOO is unconstrained, does not have the 
same effect on an allocated object.

The AARM motivates this rule as follows: "All objects created by an 
allocator are aliased, and most aliased composite objects need to be 
constrained so that access subtypes work reasonably." In other words, 
there could be a declarations like

    type PTR_T1_T is access all RECORD_T (X => T1);
    FOO_T1_PTR : PTR_T1_T;

and statements

    FOO_T1_PTR := PTR_T1_T (FOO_PTR);

Now FOO_PTR and FOO_T1_PTR point to the same object (the allocated one), 
with X = T1. If the program could now change FOO_PTR.all.X to T2, the 
subtype of FOO_T1_PTR would be a lie, because the referenced object 
would now have X = T2.

The only work-around I know of is to enclose the discriminated record in 
another record, such as:

    type HOLDER_T is record
       R : RECORD_T;
    end record;

    type PTR_T is access HOLDER_T;


-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Unexpected discriminant check failure involving access types
  2015-08-10 14:33 ` Niklas Holsti
@ 2015-08-10 14:56   ` Markus Schöpflin
  2015-08-10 19:43     ` Niklas Holsti
  0 siblings, 1 reply; 7+ messages in thread
From: Markus Schöpflin @ 2015-08-10 14:56 UTC (permalink / raw)


Am 10.08.2015 um 16:33 schrieb Niklas Holsti:
> On 15-08-10 15:38 , Markus Schöpflin wrote:
>> Given the following piece of code:
>>
>> ---%<---
>>       1  procedure TEST
>>       2  is
>>       3     type T is (T1, T2);
>>       4
>>       5     type RECORD_T (X : T := T1) is record
>>       6        null;
>>       7     end record;
>>       8
>>       9     type PTR_T is access RECORD_T;
>>      10
>>      11     FOO : RECORD_T;
>>      12     FOO_PTR : constant PTR_T := new RECORD_T;
>>      13
>>      14     FOO1 : constant RECORD_T := (X => T1);
>>      15     FOO2 : constant RECORD_T := (X => T2);
>>      16  begin
>>      17     FOO := FOO1;
>>      18     FOO := FOO2;
>>      19
>>      20     FOO_PTR.all := FOO1;
>>      21     FOO_PTR.all := FOO2;
>>      22  end;
>> --->%---
>>
>> When compiled and executed, I get:
>>
>>  > ./test
>>
>> raised CONSTRAINT_ERROR : test.adb:21 discriminant check failed
>>
>> Can anyone please explain me why I get a discriminant check error when
>> using access types? I would have expected it to work the same as for the
>> non-access case.
>
> This is a consequence of RM 4.8(6/3): "...  If the designated type is
> composite, then the subtype of the created object is the designated subtype
> when the designated subtype is constrained or there is an ancestor of the
> designated type that has a constrained partial view; otherwise, the created
> object is constrained by its initial value (even if the designated subtype is
> unconstrained with defaults)."
>
> In other words, the object FOO_PTR.all is constrained and it is not possible
> to change its discriminants -- the default value given to the discriminant X,
> which means that FOO is unconstrained, does not have the same effect on an
> allocated object.

OK, understood.

> The AARM motivates this rule as follows: "All objects created by an allocator
> are aliased, and most aliased composite objects need to be constrained so that
> access subtypes work reasonably." In other words, there could be a
> declarations like
>
>     type PTR_T1_T is access all RECORD_T (X => T1);
>     FOO_T1_PTR : PTR_T1_T;
>
> and statements
>
>     FOO_T1_PTR := PTR_T1_T (FOO_PTR);
>
> Now FOO_PTR and FOO_T1_PTR point to the same object (the allocated one), with
> X = T1. If the program could now change FOO_PTR.all.X to T2, the subtype of
> FOO_T1_PTR would be a lie, because the referenced object would now have X = T2.

Makes sense. But right now I'm surprised that

   FOO_T1_PTR := PTR_T1_T (FOO_PTR);

is actually allowed. Consider:

   FOO : aliased RECORD_T;
   FOO_T1_PTR : PTR_T1_T := PTR_T1_T'(FOO'Access);

There you get the (expected) error that the "object subtype must statically 
match the designated subtype".

> The only work-around I know of is to enclose the discriminated record in
> another record, such as:
>
>     type HOLDER_T is record
>        R : RECORD_T;
>     end record;
>
>     type PTR_T is access HOLDER_T;

Thankfully, in my case the solution was even more simple, as we were able to 
eliminate the access type altogether.

Thanks for your detailed explanation,
Markus


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Unexpected discriminant check failure involving access types
  2015-08-10 14:20   ` Markus Schöpflin
@ 2015-08-10 19:00     ` Randy Brukardt
  0 siblings, 0 replies; 7+ messages in thread
From: Randy Brukardt @ 2015-08-10 19:00 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1576 bytes --]

"Markus Schöpflin" <no.spam@spam.spam> wrote in message 
news:55C8B334.2010102@spam.spam...
...
> I still don't get it: I would have expected the objects FOO and
> FOO_PTR.all to be identical in behaviour.

You would have been wrong.

>Why is "new RECORD_T" creating a
> constrained record and just using "RECORD_T" not?

Because that's what Ada 83 specified and it is incompatible to change it. 
It's known to be stupid, but even a talented a designer as Jean Ichbiah was 
will make some mistakes.

I'm not certain as to why Ada 83 adopted that model, probably you'd have to 
look in the Rationale.

Anyway, every Ada programmer knows (or soon will find out) that one has to 
make mutable discriminated records components (never a top-level object) 
when allocated. There are a lot of records like:

     type Record_Wrapper_T is record
          C : Record_T;
     end record;

(Note that this is why the restriction is stupid, one can easily cause 
whatever complications were supposedly being eliminated by using a 
component.)

Anyway, the rule in question is 4.8(6/3), and the technical term is 
"constrained by its initial value". It's the same mechanism that is used if 
Record_T had been indefinite (had no default discriminant value).

Note that we created a "hole" in this rule for completions of (constrainted) 
private types, else there was a practical privacy breakage (some assignments 
might fail for no good reason from the clients perspective). That was 
controversal, and its unlikely we'd ever go further.

                                       Randy.




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Unexpected discriminant check failure involving access types
  2015-08-10 14:56   ` Markus Schöpflin
@ 2015-08-10 19:43     ` Niklas Holsti
  0 siblings, 0 replies; 7+ messages in thread
From: Niklas Holsti @ 2015-08-10 19:43 UTC (permalink / raw)


On 15-08-10 17:56 , Markus Schöpflin wrote:
> Am 10.08.2015 um 16:33 schrieb Niklas Holsti:
>> On 15-08-10 15:38 , Markus Schöpflin wrote:
>>> Given the following piece of code:
>>>
>>> ---%<---
>>>       1  procedure TEST
>>>       2  is
>>>       3     type T is (T1, T2);
>>>       4
>>>       5     type RECORD_T (X : T := T1) is record
>>>       6        null;
>>>       7     end record;
>>>       8
>>>       9     type PTR_T is access RECORD_T;
>>>      10
>>>      11     FOO : RECORD_T;
>>>      12     FOO_PTR : constant PTR_T := new RECORD_T;
>>>      13
>>>      14     FOO1 : constant RECORD_T := (X => T1);
>>>      15     FOO2 : constant RECORD_T := (X => T2);
>>>      16  begin
>>>      17     FOO := FOO1;
>>>      18     FOO := FOO2;
>>>      19
>>>      20     FOO_PTR.all := FOO1;
>>>      21     FOO_PTR.all := FOO2;
>>>      22  end;
>>> --->%---
>>>
>>> When compiled and executed, I get:
>>>
>>>  > ./test
>>>
>>> raised CONSTRAINT_ERROR : test.adb:21 discriminant check failed
>>>
>>> Can anyone please explain me why I get a discriminant check error when
>>> using access types? I would have expected it to work the same as for the
>>> non-access case.
>>
>> This is a consequence of RM 4.8(6/3): "... If the designated type
>> is composite, then the subtype of the created object is the
>> designated subtype when the designated subtype is constrained or
>> there is an ancestor of the designated type that has a constrained
>> partial view; otherwise, the created object is constrained by its
>> initial value (even if the designated subtype is unconstrained with
>> defaults)."
>>
>> In other words, the object FOO_PTR.all is constrained and it is
>> not possible to change its discriminants -- the default value given
>> to the discriminant X, which means that FOO is unconstrained, does
>> not have the same effect on an allocated object.
>
> OK, understood.
>
>> The AARM motivates this rule as follows: "All objects created by
>> an allocator are aliased, and most aliased composite objects need
>> to be constrained so that access subtypes work reasonably." In
>> other words, there could be a declarations like
>>
>>     type PTR_T1_T is access all RECORD_T (X => T1);
>>     FOO_T1_PTR : PTR_T1_T;
>>
>> and statements
>>
>>     FOO_T1_PTR := PTR_T1_T (FOO_PTR);
>>
>> Now FOO_PTR and FOO_T1_PTR point to the same object (the allocated
>> one), with X = T1. If the program could now change FOO_PTR.all.X to
>> T2, the subtype of FOO_T1_PTR would be a lie, because the
>> referenced object would now have X = T2.
>
> Makes sense. But right now I'm surprised that
>
>    FOO_T1_PTR := PTR_T1_T (FOO_PTR);
>
> is actually allowed.

The access-type conversion involves a run-time check that FOO_PTR.all 
satisfies the constraints for the designated type of PTR_T1_T, that is, 
that FOO_PTR.all.X = T1. I think this is specified in RM 4.6(50): "... 
any checks associated with evaluating a conversion to the target 
designated subtype are performed." Constraint_Error results if the check 
fails.

> Consider:
>
>    FOO : aliased RECORD_T;
>    FOO_T1_PTR : PTR_T1_T := PTR_T1_T'(FOO'Access);
>
> There you get the (expected) error that the "object subtype must
> statically match the designated subtype".

That's a qualified expression, not a type conversion. I suppose that 
explains the difference.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-08-10 19:43 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-10 12:38 Unexpected discriminant check failure involving access types Markus Schöpflin
2015-08-10 13:14 ` Mark Lorenzen
2015-08-10 14:20   ` Markus Schöpflin
2015-08-10 19:00     ` Randy Brukardt
2015-08-10 14:33 ` Niklas Holsti
2015-08-10 14:56   ` Markus Schöpflin
2015-08-10 19:43     ` Niklas Holsti

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox