[Bug] Internal consistency error: Incorrectly freed register in arg list
Compiling the following gives a consistency error
import meshtools
import constants
fn mobius(r = 1, ratio = 0.5) {
var L = 2*r*ratio // Separation
var m = AreaMesh(fn(u,v) mobiusfn(u,v,r=r,ntwist=-1), -Pi..Pi:Pi/20,-L/2..L/2:L/10, closed=[false,false])
return m
}
fn mobiusfn(u,v, r = 1, ntwist=1){
var th = Matrix([cos(u), 0, sin(u)])
var twist = (cos(0.5*ntwist*u)*th + sin(0.5*ntwist*u)*Matrix([0,1,0]))
return r*th + v*twist
}
--Registers (7 in use) r0 mobius [0] r1 r [0] (captured) r2 ratio [0] r3 L [1] r4 m [1] r5 temporary [1] r6 mobiusfn [1] --End registers Internal consistency error: Please contact developer. [Explanation: Incorrectly freed registers in compiling argument list.].
Reproduction script: newconsitency.zip
Putting mobiusfn before mobius does not cause issue
This should throw undefined forward reference. Forward references MUST be resolved in the same scope.
I can see what's going on here. The compiler is catching the forward reference to mobiusfn inside the anonymous function, and so allocates a register in mobius which is supposed to capture the forward reference (see compiler_addforwardreference) and become an upvalue. Unfortunately, because this takes place in an arglist, the allocated register is at the top of the stack. Once the anonymous is defined, the allocated register ready to capture mobiusfn remains at the top of the stack causing the error.
This is an edge case for forward references, which are pretty dangerous in general and I've tried to limit the extent the compiler tries to resolve them.
What I think is the right thing to do here is to prevent forward references inside arglists. I'm going to implement this now (and in any case I found a nasty bug in the course of diagnosing this). What do you think, @ConduitDan?
I think preventing forward references only inside arglist is a little unintuitive. The use of the anonymous function to lock in certain arguments of a function is not uncommon. But having a restriction that you cant do it in an arg list is not unreasonable.
If thats the way its got to be, I think throwing an error and preventing it is the right way to go.
Is there a method of defining a header to avoid this?
Currently forward references are really intended primarily to implement mutual recursive functions. The current limitation is that they have to be resolved in the scope in which they occur, i.e.
{ fn g(x) { return f(x) } fn f(x) { return g(x) } }
not
{ fn g(x) { return f(x) } }
fn f(x) { return g(x) }
I guess a forward reference inside an arglist is a bit ambiguous in terms of scope.
The problem really here is a limitation of the compiler: allowing forward references in an arglist would require the compiler to go back and change the register allocation for the function call [it would need to shift all the registers up one]. This is not trivial to implement.... so I think for now we should just prevent forward references in arglists and note that this is something we might wish to do in the future.
In this example, it's trivial (and it usually is unless mutual recursion is involved) to fix the problem by moving mobiusfn ahead of mobius.
Very happy to get feedback and ideas on this! It's a very tricky problem...
We should check back in on this
Short term: can't make a forward reference in function call Long term: can add this back in as a feature
I just checked and this edge case is still active in 0.6.1 — this is a compiler bug todo with register allocation.