comp.lang.ada
 help / color / mirror / Atom feed
* Primitive curiosity...
@ 2008-09-17 20:29 mockturtle
  2008-09-17 21:13 ` Jeffrey R. Carter
  2008-09-17 21:19 ` Adam Beneschan
  0 siblings, 2 replies; 5+ messages in thread
From: mockturtle @ 2008-09-17 20:29 UTC (permalink / raw)


Dear *,
I was doing some experiments with dispatching operations
and I discovered something that made me curious...  Consider
the following 3-file set: 1 package (base.ad?)  with a
"root" abstract type (Root) and two derived types
(Derived_1 and Derived_2) and 1 "main" (test_bas.adb)

----------------------- Filename : base.ads ----------------
package Base is
   type Root is abstract tagged null record;

   -- Uncomment LINE A or LINE B, but not both
   -- === LINE A ===
   -- procedure Print(X : Root) is abstract;

   type Derived_1 is new Root with
      record
         A : Integer;
      end record;

   type Derived_2 is new Root with
      record
         B : Float;
      end record;

   -- === LINE B ===
   -- procedure Print(X : Root) is abstract;

   procedure Print(X : Derived_1);
   procedure Print(X : Derived_2);
end Base;

------------------ Filename : base.adb ----------------------
with Ada.Text_Io;

package body Base is
   procedure Print(X : Derived_1) is
   begin
      Ada.Text_Io.Put_Line ("[1] " & Integer'Image(X.A));
   end Print;

   procedure Print(X : Derived_2) is
   begin
      Ada.Text_Io.Put_Line ("[2] " & Float'Image(X.B));
   end Print;
end Base;

-------------------------------------------------------------
----------------- Filename : test_base.adb -----------
-------------------------------------------------------------
with Base;

procedure Test_Base is
   function Gimme (X : Boolean)
		  return Base.Root'Class is
   begin
      if (X) then
         return Base.Derived_1'(A => 42);
      else
         return Base.Derived_2'(B => 3.1415);
      end if;
   end Gimme;
begin
   Base.Print(Gimme(True));
   Base.Print(Gimme(False));
end Test_Base;
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------

The idea is that the first line in Test_Base should call
Print of Derived_1 and the second line should call
procedure Print of Derived_2.  In order to compile, one must
uncomment LINE A or LINE B (but not both) in
base.ads.

Now the fun bit comes

  1) If I uncomment LINE A everything is fine, but...
  2) if I uncomment LINE B gcc (4.1.3) complaints with
    .... other lines ....
    base.ads:19:14: this primitive operation is declared too late

I interpreted the "immediately within" in the sentence
[AARM05 3.2.3(2/6)]

   "The primitive subprograms of a specific type are
   defined as follows:
   [...]
   * For a specific type declared immediately within
   a package_specification, any subprograms
   (in addition to the enumeration literals) that are
   explicitly declared  _immediately_within_  the same
   package_specification and that operate on the type;"

as "declared in the same file as the type", but according
to the behaviour described above (and some other
experiments) it seems that there is
a further constraint "before the declaration of any
derived type."

Is this actually implied by the RM description or is gcc
being strict? (Maybe "immediately within" was given a
technical sense, but I was not able to find the definition)

Why this?  There is some deep technical reason or is it
just to enforce the programmer to be "order" and declare
primitive subprograms close to the type definition?

Thank you in advance



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

* Re: Primitive curiosity...
  2008-09-17 20:29 Primitive curiosity mockturtle
@ 2008-09-17 21:13 ` Jeffrey R. Carter
  2008-09-17 21:19 ` Adam Beneschan
  1 sibling, 0 replies; 5+ messages in thread
From: Jeffrey R. Carter @ 2008-09-17 21:13 UTC (permalink / raw)


mockturtle wrote:
> 
> Is this actually implied by the RM description or is gcc
> being strict? (Maybe "immediately within" was given a
> technical sense, but I was not able to find the definition)
> 
> Why this?  There is some deep technical reason or is it
> just to enforce the programmer to be "order" and declare
> primitive subprograms close to the type definition?

This has to do with "freezing". When a type is frozen, all primitive operations 
of the type have been declared.

See ARM 3.9.2.13: "The explicit declaration of a primitive subprogram of a 
tagged type shall occur before the type is frozen (see 13.14). For example, new 
dispatching operations cannot be added after objects or values of the type 
exist, nor after deriving a record extension from it, nor after a body."

Deriving an extension from the type freezes it.

-- 
Jeff Carter
"Apart from the sanitation, the medicine, education, wine,
public order, irrigation, roads, the fresh water system,
and public health, what have the Romans ever done for us?"
Monty Python's Life of Brian
80



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

* Re: Primitive curiosity...
  2008-09-17 20:29 Primitive curiosity mockturtle
  2008-09-17 21:13 ` Jeffrey R. Carter
@ 2008-09-17 21:19 ` Adam Beneschan
  2008-09-18 19:27   ` mockturtle
  2008-09-24 14:02   ` Martin
  1 sibling, 2 replies; 5+ messages in thread
From: Adam Beneschan @ 2008-09-17 21:19 UTC (permalink / raw)


On Sep 17, 1:29 pm, mockturtle <framefri...@gmail.com> wrote:
> Dear *,
> I was doing some experiments with dispatching operations
> and I discovered something that made me curious...  Consider
> the following 3-file set: 1 package (base.ad?)  with a
> "root" abstract type (Root) and two derived types
> (Derived_1 and Derived_2) and 1 "main" (test_bas.adb)

[source code snipped]

> The idea is that the first line in Test_Base should call
> Print of Derived_1 and the second line should call
> procedure Print of Derived_2.  In order to compile, one must
> uncomment LINE A or LINE B (but not both) in
> base.ads.
>
> Now the fun bit comes
>
>   1) If I uncomment LINE A everything is fine, but...
>   2) if I uncomment LINE B gcc (4.1.3) complaints with
>     .... other lines ....
>     base.ads:19:14: this primitive operation is declared too late
>
> I interpreted the "immediately within" in the sentence
> [AARM05 3.2.3(2/6)]
>
>    "The primitive subprograms of a specific type are
>    defined as follows:
>    [...]
>    * For a specific type declared immediately within
>    a package_specification, any subprograms
>    (in addition to the enumeration literals) that are
>    explicitly declared  _immediately_within_  the same
>    package_specification and that operate on the type;"
>
> as "declared in the same file as the type", but according
> to the behaviour described above (and some other
> experiments) it seems that there is
> a further constraint "before the declaration of any
> derived type."

I realize this post wasn't addressed to me, since my name is /, not
*.  Or maybe it's %.  Anyway, though, the error message is correct.
See 13.14(16) and 13.14(7).

What's going on here is this: A type is "frozen" when it is used in
certain contexts; the idea is that the representation of the type must
have been completely determined by the time it's used in one of these
contexts, and you can't write (say) a representation clause for one of
those types after the compiler has already used the information.  For
example, if you declare a variable V of a type T, the compiler will
allocate space for the variable V; if you then do something after the
variable declaration that could change the size of T, the compiler
will tear its hair out and complain "Why didn't you tell me that
earlier???!!!"  Or, at least that's approximately how I'd react if my
wife did that to me.  But anyway, that's the whole point of the
freezing rules.

For a tagged type, the things that need to be determined before it's
frozen include something sometimes called the "dispatch vector" or
"dispatch table" or something like that.  It's basically a table that
contains the addresses of the primitive subprograms of the type.  Type
extensions that may override those subprograms will have their own
array with the primitive subprograms in the same slots in the array.
Then, when you do a dispatching operation, the code generated by the
compiler will just look in that slot in the array to see what
procedure to execute.  That's how polymorphism takes place.

But this requires that the dispatch table be completed before it's
used.  When you declare a type extension, it will inherit the root
type's primitive operations, but you can declare *new* operations of
the type extension (and its descendants), which will need to be put
into new slots in the array.  For this to happen, the compiler has to
know already how many slots will be allocated to the primitive
subprograms of the parent type.  The number of subprograms of the
parent type is then frozen, so that the compiler can safely allocate
slots beyond that for operations of the child types.

Therefore, if the language let you declare a new primitive subprogram
of the parent type after the compiler already needed to know how many
primitive subprograms there were going to be, it would screw
everything up, because the compiler already thought it knew how many
primitive operations of the parent type there were going to be, and
here you are adding a new one, and the compiler can't do anything
except tell you "You shoulda told me about that earlier".  Which, in
effect, what the error message is doing.

Hope this helps explain things,

                              -- Adam




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

* Re: Primitive curiosity...
  2008-09-17 21:19 ` Adam Beneschan
@ 2008-09-18 19:27   ` mockturtle
  2008-09-24 14:02   ` Martin
  1 sibling, 0 replies; 5+ messages in thread
From: mockturtle @ 2008-09-18 19:27 UTC (permalink / raw)


Jeff and Adam,
thank you to both of you for your explanation.  Now everything is
clear.  "Freezing" did not come to my mind (To be honest: I never
read that part of our beloved/behated RM...) and (now) it is quite
obvious why it is not possible to add more primitive operations
after freezing the type.

Thank you again



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

* Re: Primitive curiosity...
  2008-09-17 21:19 ` Adam Beneschan
  2008-09-18 19:27   ` mockturtle
@ 2008-09-24 14:02   ` Martin
  1 sibling, 0 replies; 5+ messages in thread
From: Martin @ 2008-09-24 14:02 UTC (permalink / raw)


On 17 Sep, 22:19, Adam Beneschan <a...@irvine.com> wrote:
[snip]
> For a tagged type, the things that need to be determined before it's
> frozen include something sometimes called the "dispatch vector" or
> "dispatch table" or something like that.  It's basically a table that
> contains the addresses of the primitive subprograms of the type.  Type
> extensions that may override those subprograms will have their own
> array with the primitive subprograms in the same slots in the array.
> Then, when you do a dispatching operation, the code generated by the
> compiler will just look in that slot in the array to see what
> procedure to execute.  That's how polymorphism takes place.

Those from a C++ background may think this sounds like a "vtable"...




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

end of thread, other threads:[~2008-09-24 14:02 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-09-17 20:29 Primitive curiosity mockturtle
2008-09-17 21:13 ` Jeffrey R. Carter
2008-09-17 21:19 ` Adam Beneschan
2008-09-18 19:27   ` mockturtle
2008-09-24 14:02   ` Martin

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