[M3devel] opaque types again --- A Correction

Rodney M. Bates rodney_bates at lcwb.coop
Sun Mar 31 19:40:23 CEST 2013



On 03/31/2013 11:50 AM, Rodney M. Bates wrote:
>
>
> On 03/31/2013 12:10 AM, Jay K wrote:
>> I don't understand opaque types.
>>
>
> It can be tricky.
>
>>
>> I do understand the notion of fully opaque types. That are fully revealed in one step.
>> In C this is common:
>>
>>
>>    window.h
>>      struct Window_t;
>>      typedef struct Window_t Window_t;
>>      Window_t* Window_Create();
>>      void Window_Show(Window_*t);
>>      void Window_Close(Window_*t);
>>      long Window_GetHeight(Window_*t);
>>      long Window_GetWidth(Window_*t);
>>    etc.
>>
>>    window.c
>>      struct Window_t { ... } /* reveal */
>>
>>
>> Back to Modula-3... opaque types must be OBJECTs, right?
>>
>
> It's slightly more liberal than that.  They can be any reference type, which
> includes object types and REF Mumble, for any Mumble.  But since the subtype
> hierarchy has only two levels for REF types (REFANY and REF Mumble), you can
> only get fully opaque and fully revealed, as in the C example above.
>
>>
>> 1. What do they provide vs. more derived types?
>>
>>
>> INTERFACE Animal;
>>
>> TYPE T = OBJECT
>> METHODS
>>      makeNoise();
>> END;
>>
>>
>> INTERFACE AnimalImpl;
>>
>> TYPE T = Animal.T OBJECT
>> METHODS
>>      private();
>> END;
>>
>>
>> If I had an Animal.T and wanted to call private(),
>> wouldn't I just NARROW it to AnimalImpl.T?
>>
>
> If you allocate VAR V : Animal.T := NEW (Animal.T), the result has allocated type Animal.T, not AnimalImpl.T.
> So it has no method private, and even with AnimalImpl imported, trying to narrow it to AnimalImpl.T will
> suffer a runtime error.
>
> If you do it this way: VAR V : Animal.T := NEW (AnimalImpl.T), V still has static type Animal.T, but
> its runtime value is (for now, at least) an object with allocated type AnimalImpl.T, which has method
> private, and you can thus narrow it to AnimalImpl.T and call private.
>
>>
>> Is the point that it is more efficient and avoids
>> a runtime type check?
>>
>
> That's part of it.
>
>>
>>
>> INTERFACE Animal;
>>
>> TYPE Public = OBJECT
>> METHODS
>>      makeNoise();
>> END;
>>
>> TYPE T <: Public;
>>
>> INTERFACE AnimalImpl;
>>
>> REVEAL Animal.T = Animal.Public OBJECT
>> METHODS
>>      private();
>> END;
>>
>>
>
> Here, even in a context where it is not revealed, if you allocate VAR V : Animal.T := NEW (Animal.T),
> the allocated type has method private.  Note that Animal.T here is the same type as AnimalImpl.T is
> in the first example.  You just don't know all there is to know about what type that is.
>
> So the object still sits in the heap with all the fields and methods of the final type.  If you
> import AnimalImpl, this gives more (static) information about the same type Animal.T instead of just
> a different type.  So the revelation would make it statically legal to call V.private(), without needing a
> narrow and without a runtime check.  It is statically known everywhere Animal is imported, that
> objects of Animal.T have all the properties of the final revealed type, even though it's not knowy what
> they all are.
>
> Of course, you could also do this: VAR W : Animal.Public := NEW (Animal.Public), and that would
> behave just like Animal.T of the first example, since those are the same type.  Usually, you would probably
> not want to use Animal.Private in that way.  It's something like a variation on the theme of an abstract
                   Animal.Public
> supertype that you will never allocate.
>
> Sometimes I think it would help clarity if you could define a reference type as ABSTRACT, meaning it's
> illegal to allocate it.  But that might open a can of worms in the language.  Hmm, maybe I'll work on
> remembering to write (*ABSTRACT*) OBJECT ... I think I even did that once.
>
>> ?
>> This way I can import AnimalImpl and then just call private()
>> without a NARROW?
>>
>>
>
> Yes
>
>>
>>
>>
>> 2. Given that the final revelation must be a linear
>> form of all the declared subtypes, I don't understand
>> how the offset used by any module w/o a full revelation
>> could be anything other than zero.
>>
>
> Not sure this helps much, but a complication comes from the fact that you can declare a truly new subtype
> (not just reveal more about an existing opaque type), in a context where the opaque type is not fully revealed.
>
> IMPORT Animal (* The second version of Animal above. *)
> TYPE Cat = Animal.T OBJECT
>     IsDeclawed : BOOLEAN
>     END;
>
> The compiler will not know while separately compiling this, the offset, relative to Animal.T, where new fields of
> Cat will start.  I have never examined the way this is implemented.  I would think a typical linker's relocation
> or external symbol mechanism could be used to add to the offset, a component that is defined in another compilation.
> I think Tony understands how this is implemented.  I have noted that compilations sometimes tell us they are
> recompiling some module because new opaque information has become available.
>
>>
>> I'd like to fully understand this, so I can make the C backend
>> provide maximal type information. More pointers to structs, with members/fields,
>> fewer untyped void*/char*.
>>
>
> If you are hoping to express Modula-3's actual static information hiding in C, I suspect that is a Quixotic quest.
> At the very least, you would have to replace what is really a flat struct with lots of nesting of structs for
> the different static visibility groups, which would just hurt readability elsewhere, in references to fields.
> By definition, it is a lower-level form of code here.  I'd just arrange to get the full, flat struct available
> anywhere it is needed, and rely on the fact that the M3 front end will already have refused to pass down any
> references to fields that aren't statically legal in the M3 source code.  There is certainly no need to get
> the C compiler to redundantly enforce this.
>
>>
>> Thanks,
>>   - Jay
>>
>>
>
>




More information about the M3devel mailing list