[m-dev.] Abstract exported dummy types

Julien Fischer jfischer at opturion.com
Thu Oct 19 10:53:51 AEDT 2017


Hi Zoltan,

On Thu, 19 Oct 2017, Zoltan Somogyi wrote:

> Mantis bug 441 raises some interesting questions I would like your opinions on.
>
> The bug test case has a module (vfm2.m) that abstract exports a type (fio(T))
> that it privately defines to be a dummy type. When compiling that module,
> the compiler knows that fio(T) and its instances are dummy types, so
> when a function returns such an instance, it makes the return type
> of the generated C function void.

To be more accurate, fio(T) is:

    1. an abstract type
    2. a notag type
    3. some instances of it can be dummy types, e.g. fio(unit)

vfm2.m is a cut-down version of the original program; the original also
exported functions that instantiate the type variable T to other
(non-dummy) types.

> The other module in the program, not knowing that vfm2.m privately
> defines fio(T) to be a dummy type, expects a real, nonvoid return
> value. The root cause of the bug is therefore the fact that the two
> modules make inconsistent assumptions about whether
> instances of fio(T) are dummy types or not.
>
> I know a way to fix the problem, but I am not sure we want to pay the cost,
> because the proposed fix is complicated. 
>
> Suppose we have a module x.m
>
> - that defines a type t that it knows is a dummy type, but other modules
>  don't know that, because it abstract exports t,
> - that x.m defines a function f that has at least one argument of type t, and
> - function is exported, and it is also called from within x.m.
>
> Question is: should the signature of the C function we generate for f reflect
> the fact that t is dummy or not?
>
> Neither answer works. If we say it should, then callers from outside the module
> will do the wrong thing; if we say it shouldn't, then callers from inside the module
> will do the wrong thing.
>
> One thing I think can do in such cases is to create a new inner function
> for every function f whose argument list includes a type that is known to be dummy
> only in this module. The original function would be compiled as if t were *not* dummy,
> and this would be the function that is exported. Its implementation would call the
> newly created inner function, which would treat t as a dummy type, and all calls 
> to f in x.m would be updated to refer to it. This would preserve the speedup we get
> from not passing dummy arguments around inside x.m. However, this approach
> has a problem: what do we do with references to f other than calls? If some other
> function g takes f's address and constructs a closure with it, that closure can potentially
> be called both inside x.m and outside it, and we are back where we started, with the
> original problem. We can solve this new instance of the problem, e.g. by decreeing
> that when generating higher order calls, we always pass all arguments including
> the ones that look like dummy types to us (since they may look like nondummy types
> to other users of the closure), but that is a solution I don't much like.

That seems quite complicated (and likely brittle).

> In any case, the approach above does not allow code passing around values of type t
> to be optimized outside x.m. We could fix this in one of two ways.
>
> One way would for us to put a pragma in x's interface files to say that f is a dummy type.

Presumably, you mean add the pragma if _t_ is a dummy type?  Or would
the pragma be on the function that returns the dummy type?

> The other would be simply to generate an error when a dummy type is abstract exported,
> requiring the programmer to choose between exporting the definition of the dummy type
> and keeping the type private but making it non-dummy.

Removing the abstraction barrier may be potentially unsafe (in this case
it definitely is); making this type non-dummy (in the case where T is
dummy) could only be done by making it a non notag type which would
incur a memory allocation everytime a (non dummy) value is wrapped.

> Note that other modules would need to be recompiled when either an exported dummy type
> is changed (with the second way above), or when a nonexported type is changed either
> from a dummy type to a nondummy type or vice versa. So the two ways above have similar
> performance implications; the difference being that the second is in a way more "honest"
> about the costs (and the benefits) of exposing the dummy-ness of the type.

Is it possible to not apply the dummy type optimization to abstract
exported types (i.e. the dummy type optimization would not apply in both
the defining module and any importing ones)?

> I have a slight preference for the last solution above: generating
> errors for abstract-exported dummy types.

What would the error message be generated for?  One of the type
declarations or the function return types?

> Judging by the absence of previous bug reports similar to Mantis 441,
> most people won't even notice this new rule.

It requires a fairly specialised set of circumstances to trip over it.

Julien.


More information about the developers mailing list