comp.lang.ada
 help / color / mirror / Atom feed
From: Adam Beneschan <adam@irvine.com>
Subject: Re: Unknown discriminants with nested records
Date: Thu, 16 Jun 2011 17:39:20 -0700 (PDT)
Date: 2011-06-16T17:39:20-07:00	[thread overview]
Message-ID: <e3d8de4a-b442-4494-abef-4fe6c3eb7094@r27g2000prr.googlegroups.com> (raw)
In-Reply-To: 1e197c14-a2ff-4867-85be-fda06a10e37b@a7g2000vby.googlegroups.com

On Jun 16, 4:53 pm, Simon Belmont <sbelmont...@gmail.com> wrote:
> New to ada (2005), and trying to figure out how to create records of
> other records with unknown discriminants, e.g. like this:
>
> package Test_Package is
>
>     type Foo_Type (<>) is limited private;  -- some record with
> unknown discriminants.
>
>     t1: Foo_Type := MakeAFooType; -- works fine being built by a
> function
>
>     type BarType is
>     record
>      B1: Integer;
>      -- other record elements
>      B2: FooType := MakeAFooType;  -- Error, unconstrained subtype.
>     end record;
>
> private
>
>     type FooType is limited
>     record
>      F1: Integer;
>     end record;
>
> end Test_Package;
>
> The (<>) seems to be the defacto way to limit creation to a function,
> yet it seemingly precludes you from using it in a nested record.  It
> seems strange that there is the ability to declare a standalone object
> of that type, but trying to nest one within another record throws an
> "unconstrained subtype" error; if it knows the constraints one place,
> why doesn't it know them several lines down?

To start with, it's helpful to post the actual source you tried.
Clearly this isn't, since you didn't spell Foo_Type consistently, but
the more important problem is that you've stuck the declarations T1
and BarType in the middle of Test_Package where they don't belong.
You can't declare T1 to be a Foo_Type at that point, because
Foo_Type's definition hasn't been finished.  Anyway, I'll assume that
T1 and BarType are actually declared in some other package
Test_Package_2 that with's and uses Test_Package.

The basic problem is this: When Test_Package_2 is elaborated, and it
elaborates the definition of T1, it will call MakeAFooType at that
point.  The discriminants (if any) of the value will then be
determined, so T1's discriminants will then be known.

But when BarType is elaborated, MakeAFooType isn't called.  The use of
MakeAFooType here is a *default* expression.  When you later declare

   B : BarType;

with no initial expression, MakeAFooType will be called at *that*
point, when B is elaborated.  It's also possible to declare B with an
initial expression like

   B : BarType := (B1 => 1, B2 => Make_A_Different_Kind_Of_Foo);

so that MakeAFooType wouldn't be called at all.  So here, the
constraints of the B2 component won't be what MakeAFooType would
return.

Hopefully that will answer your question about "why doesn't it know
[the constraints] several lines down".  It doesn't know because that
syntax doesn't actually result in a function call.

The problem here is that since the initial expression on the component
is just a *default* and may not be the actual initial expression for a
B2 component, the compiler thus doesn't know what constraints a B2
will have.  In theory, since it's possible for objects with different
constraints to have different sizes, the compiler thus may not know
how big a BarType will be, and I think that's the technical reason why
this sort of component type is prohibited.  (In practice, it might be
possible to make this work, since often the size of a FooType won't
vary; but it might be hard to do without introducing some incompatible
changes in the language.  I don't know if any possible language
changes have ever been discussed.)

As for how to get around this: if it were me, I'd just lose the (<>).
The (<>) was intended to define types with unknown discriminants;
using it to force initialization functions to be used, when you know
there won't really be any discriminants, is using this feature for a
purpose for which it wasn't intended, I think.  Someone clever found
that it works for this purpose, but obviously it doesn't work all the
time.  Maybe a language feature should be added that allows the user
to define a *definite* type that still isn't allowed to be
uninitialized.  Here's a possible way to force all uses of this type
to be initialized:

package Test_Package is
    type Foo_Type is limited private;
    ... etc. etc.
private
    function Blow_Up return Integer;
    type Foo_Type is limited
       record
          F1 : Integer := Blow_Up;
       end record;
end Test_Package;

where Blow_Up is defined like:

    function Blow_Up return Integer is
    begin
       raise Program_Error with "You forgot to initialize your
Foo_Type";
       return 0;  -- never executed
    end Blow_Up;

or something along those lines.  This won't be caught at compile time
(although some compilers may display a warning that Program_Error will
be raised), but a program that declares a variable that is or contains
an uninitialized Foo_Type will bomb.


> 4. Can anyone point me to the section in the LRM that specifies this
> either way?

3.6(10).  This is in the section on array types, but the restriction
on a component's type applies to record type components too.

                                 -- Adam



  reply	other threads:[~2011-06-17  0:39 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-16 23:53 Unknown discriminants with nested records Simon Belmont
2011-06-17  0:39 ` Adam Beneschan [this message]
2011-06-17  1:48   ` Simon Belmont
2011-06-17 14:50     ` Robert A Duff
2011-06-18  8:33       ` Yannick Duchêne (Hibou57)
2011-06-20 16:16         ` Adam Beneschan
2011-06-17  6:03   ` Jeffrey Carter
replies disabled

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