[m-dev.] Abstract exported dummy types
zoltan.somogyi at runbox.com
Thu Oct 19 00:37:18 AEDT 2017
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. 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.
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.
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.
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.
I have a slight preference for the last solution above: generating errors for abstract-exported
dummy types. Judging by the absence of previous bug reports similar to Mantis 441, most people
won't even notice this new rule.
What does everyone else think?
More information about the developers