[M3devel] reference to globals in globals?

Rodney M. Bates rodney_bates at lcwb.coop
Sat Aug 18 19:51:11 CEST 2012



On 08/15/2012 05:07 PM, Jay K wrote:
>    > If fact, if you are accustomed to thinking in type-safe terms
>
>
> I do think in fairly type-terms.
> In C++ I know to use templates, for example.
> There is a matter of lifetime-safety though.
> I understand that they are related -- if I get the lifetime wrong, then I end up with the type wrong, e.g. using freed-and-possibly-reallocated memory as if it was what it used to be and what I thought it still is.
>
>
> Consider C++ code like this, where I want to do some things
> "the same" to a bunch of variables, and some things "specific".
>
>
> struct Person_t { const char* name; };
>
> void F()
> {
> Person_t jay {"jay"};
> Person_t randy = {"randy"};
> Person_t tony = {"tony"};
>
> // print all their names
>    Person_t* people[] = {&jay, &randy, &tony};
>    for (i = 0; i < sizeof(people) / sizeof(people[0]; ++i)
>      printf("%s\n", people[i]->name);
>
>     DoSomethingSpecificToJay(&jay);
>     DoSomethingSpecificToRandy(&randy);
> DoSomethingSpecificToTony(&tony);
>
> }
>

In Modula-3, you can code like this too, but you have to declare it UNSAFE
and use unsafe constructs, just like you have to use in C, but don't have
to admit explicitly .

> I do NOT want to do this:
>
> void F()
> {
>    Person_t people[3] = {{"jay"}, {"randy"}, {"tony"}};
>
>
>   // print all their names
>    for (i = 0; i < sizeof(people) / sizeof(people[0]; ++i)
>       printf("%s\n", people[i].name);
>
>     DoSomethingSpecificToJay(&people[0]);
>     DoSomethingSpecificToRandy(&people[1]);
> DoSomethingSpecificToTony(&people[2]);
>
>   }
>
>   I understand there is:
> enum People { jay, randy, tony };
> ...
>     DoSomethingSpecificToJay(&people[jay]);
>     DoSomethingSpecificToRandy(&people[randy]);
> DoSomethingSpecificToTony(&people[tony]);
> ...
>
>
>   but I'm still not thrilled about that.
>
> I ended up without anything "specific" except initialization..so no
> really need to hang a name/variable on the data, and the
> initialization is fairly specific, so the resulting code is merely:
>
>
>      EVAL Type_Init(NEW(Integer_t, cg_type := Target.Integer.cg_type, typeid := UID_INTEGER));
>      EVAL Type_Init(NEW(Integer_t, cg_type := Target.Word.cg_type, typeid := UID_WORD));
>      EVAL Type_Init(NEW(Integer_t, cg_type := Target.Int64.cg_type, typeid := UID_LONGINT));
>      EVAL Type_Init(NEW(Integer_t, cg_type := Target.Word64.cg_type, typeid := UID_LONGWORD));
>
>      EVAL Type_Init(NEW(Float_t, cg_type := Target.Real.cg_type, typeid := UID_REEL));
>      EVAL Type_Init(NEW(Float_t, cg_type := Target.Longreal.cg_type, typeid := UID_LREEL));
>      EVAL Type_Init(NEW(Float_t, cg_type := Target.Extended.cg_type, typeid := UID_XREEL));
>
>      EVAL Type_Init(NEW(Enum_t, cg_type := Target.Word8.cg_type, typeid := UID_BOOLEAN, max := 1));
>      EVAL Type_Init(NEW(Enum_t, cg_type := Target.Word8.cg_type, typeid := UID_CHAR, max := 16_FF));
>      EVAL Type_Init(NEW(Enum_t, cg_type := Target.Word16.cg_type, typeid := UID_WIDECHAR, max := 16_FFFF));
>
>      EVAL Type_Init(NEW(Subrange_t, cg_type := Target.Integer.cg_type, typeid := UID_RANGE_0_31, min := 0, max := 31));
>      EVAL Type_Init(NEW(Subrange_t, cg_type := Target.Integer.cg_type, typeid := UID_RANGE_0_63, min := 0, max := 31));
>
>   ...
>
> you can see it in m3back/src/M3C.m3 (which tangentially I think strikes at possible problems
> in the m3cg interface...these types need to be defined via function calls into the backend,
> not hardcoded..and even if they are...the order of types arriving at the backend isn't always
> ideal..types maybe come in before they are referenced..maybe that is unavoidable due to loops...
> I suspect I need to put in the multiple passes like I did in parse.c...we'll see...)
>
>
>   - Jay
>
>
>  > From: jay.krell at cornell.edu
>  > Date: Wed, 15 Aug 2012 12:56:09 -0400
>  > To: hosking at cs.purdue.edu
>  > CC: m3devel at elegosoft.com; jay.krell at cornell.edu
>  > Subject: Re: [M3devel] reference to globals in globals?
>  >
>  > I restructured the code but it still bothers me. Getting the levels of indirection correct is checked for you in C/C++ as '&' returns a stronger type than 'ADR'.
>  > I didn't want the array only because then I could only access the data more verbosely/slowly via the array. More later, maybe.
>  >
>  > - Jay (briefly/pocket-sized-computer-aka-phone)
>  >
>  > On Aug 15, 2012, at 12:11 PM, Tony Hosking <hosking at cs.purdue.edu> wrote:
>  >
>  > > Jay,
>  > >
>  > > Any time you want to pass a reference to a local/global as a parameter you can use VAR/READONLY parameter mode.
>  > >
>  > > I don’t know enough about your use-case to understand what you are trying to do.
>  > >
>  > >
>  > > On Aug 15, 2012, at 10:51 AM, "Rodney M. Bates" <rodney_bates at lcwb.coop> wrote:
>  > >
>  > >>
>  > >>
>  > >> On 08/14/2012 10:04 PM, Jay K wrote:
>  > >>> Isn't it safe to take the address of a global?
>  > >>>
>  > >>
>  > >> Do you mean can't you use the ADR function in safe code
>  > >> if you apply it only to a global variable? The answer
>  > >> to that is no. The ADR function is illegal altogether in
>  > >> safe code.
>  > >>
>  > >> As to why, I can only speculate, but see below. I suspect
>  > >> even in this case, it is not as simple as it seems.
>  > >>
>  > >>>
>  > >>> I have something like this:
>  > >>>
>  > >>>
>  > >>> CONST UID_INTEGER = 1234;
>  > >>> CONST UID_FLOAT = 4567;
>  > >>> ... several more ...
>  > >>>
>  > >>>
>  > >>> TYPE CType = OBJECT .. END;
>  > >>>
>  > >>>
>  > >>> VAR t_int: CType := ...;
>  > >>> VAR t_float: CType := ...;
>  > >>> ... several more ...
>  > >>>
>  > >>>
>  > >>> MapTypeIdToType(UID_INTEGER, t_int);
>  > >>> MapTypeIdToType(UID_FLOAT, FLOAT);
>  > >>> ... several more ...
>  > >>>
>  > >>>
>  > >>> but what I really want is more like:
>  > >>>
>  > >>>
>  > >>> TYPE RECORD = BuiltinUid_t =
>  > >>> typeid: INTEGER;
>  > >>> ctype: REF CType;
>  > >>
>  > >> ^UNTRACED REF? If it were just REF, that would imply that
>  > >> your global variable (the pointer it contains) is a heap object, that
>  > >> it has heap allocator/GC overhead data attached to it, and that the GC
>  > >> should trace it, none of which is true.
>  > >>
>  > >>
>  > >>> END;
>  > >>>
>  > >>>
>  > >>> CONST BuiltinUids = ARRAY OF BuiltinUids {
>  > >>> BuiltinUids{UID_INTEGER, &t_int},
>  > >>> BuiltinUids{UID_FLOAT, &t_float},
>  > >>
>  > >> ADR instead of &? If so, you are still not there, because ADR
>  > >> returns a value of type ADDRESS, i.e., an untraced reference to
>  > >> we-don't-know-what. Somewhere, you would also have to use a
>  > >> LOOPHOLE to get it to UNTRACED REF CType.
>  > >>
>  > >>> ... several more ...
>  > >>> };
>  > >>>
>  > >>>
>  > >>> FOR i := FIRST(BuiltinUids) TO LAST(BuiltinUids) DO
>  > >>> MapTypeIdToType(BuiltinUids[i].typeid, BuiltinUids[i].ctype);
>  > >>> END;
>  > >>>
>  > >>
>  > >> I don't know what the signature of MapTypeIdToType is, but above,
>  > >> you are passing a variable of object type to its 2nd parameter,
>  > >> (which contains a traced reference to the actual heap object).
>  > >> But here, you pass the _address_ of the above. Inconsistent
>  > >> number of levels of indirection. A static safe language is
>  > >> much more likely to help with things like this.
>  > >>
>  > >> Maybe you just want to say
>  > >>
>  > >> TYPE RECORD = BuiltinUid_t =
>  > >> typeid: INTEGER;
>  > >> ctype: CType;
>  > >>
>  > >> and
>  > >>
>  > >> BuiltinUids{UID_INTEGER, t_int}?
>  > >>
>  > >> This would be equivalent to your first way, and doesn't require any
>  > >> unsafe coding at all.
>  > >>
>  > >> Or, you could do away with global variable t_int altogether and
>  > >> just initialize directly into BuiltinUids[..].ctype with whatever
>  > >> expression you used to initialize t_int. It looks like your array
>  > >> makes the t_int and cousins redundant.
>  > >>
>  > >>>
>  > >>> Heck, even if these weren't global, is it that unreasonble,
>  > >>> from the programmer's point of view, for the language/compiler
>  > >>> to do some pointer escape analysis and let me take the address
>  > >>> of a local, as long as I don't store it somewhere that outlives
>  > >>> the local?
>  > >>>
>  > >>
>  > >> This is ultimately an undecidable problem and even conservative
>  > >> approximations of reasonable sophistication are far too involved
>  > >> for a language to require of every compiler.
>  > >>
>  > >>>
>  > >>> You can see this particular pattern currently in
>  > >>> m3-sys/m3cc/gcc/gcc/m3cg/parse.c
>  > >>>
>  > >>>
>  > >>> and I'm pretty busy now working on m3-sys/m3back/src/M3C.m3
>  > >>> where I encounter this.
>  > >>>
>  > >>>
>  > >>> Working in safe languages can be frustrating...
>  > >>>
>  > >>
>  > >> It's just an instant/deferred gratification thing. Safe languages often
>  > >> make you stop and fix it before you run it. Unsafe languages let you naively
>  > >> forge ahead to the next step, where the bug is likely to be *much* harder to
>  > >> diagnose, assuming you even have enough test cases to notice it at all during
>  > >> development. Your code here is a good example.
>  > >>
>  > >> Of course, safe languages occasionally make you unnecessarily write a bit more
>  > >> code to do it the safe way. E.g., the famous fake pointer to the root of a
>  > >> linked list example. In my personal experience, these times are at least
>  > >> one order of magnitude less frequent than the times safe languages reduce
>  > >> great pain to minor pain, albeit sooner. If fact, if you are accustomed to
>  > >> thinking in type-safe terms, it is seldom any harder to code it safely in the
>  > >> first place.
>  > >>
>  > >> You're making it too difficult.
>  > >>
>  > >>>
>  > >>> Thank you,
>  > >>> - Jay
>  > >>
>  > >




More information about the M3devel mailing list