Closed ShawSumma closed 3 months ago
This issue is breaking temper-regex-engine right now, and it used to work. I suspect it's a somewhat recently introduced bug. Maybe the new decompiler has a hole in it somewhere.
Below is the IR code at the end of GenerateCode. It looks like any mistransformation is probably during TmpL translation.
@reach(\none) let mod1__0, @reach(\none) mod2__0, @reach(\none) mod3__0;
mod1__0 = (@stay fn mod1(n__0 /* aka n */: Int, d__0 /* aka d */: Int) /* return__0 */: (Int | Bubble) {
var fail#0;
if (d__0 == 0) {
bubble()
} else {
return__0 = hs(fail#0, n__0 % d__0);
if (fail#0) {
bubble()
}
}
});
mod2__0 = (@stay fn mod2(n__1 /* aka n */: Int, d__1 /* aka d */: Int) /* return__1 */: (Int | Bubble) {
var fail#1;
if (!(d__1 == 0)) {
return__1 = hs(fail#1, n__1 % d__1);
if (fail#1) {
bubble()
}
} else {
bubble()
}
});
mod3__0 = (@stay fn mod3(n__2 /* aka n */: Int, d__2 /* aka d */: Int) /* return__2 */: (Int | Bubble) {
fn__0: do {
var fail#2;
if (!(d__2 == 0)) {
return__2 = hs(fail#2, n__2 % d__2);
if (fail#2) {
bubble()
};
break fn__0;
};
bubble()
}
})
Here's the TmpL translation:
//// foo.temper => foo.tmpl
let nym`==#32` = builtins.nym`==` /* fn (lhs: Int | Null, rhs: Int | Null): Boolean */;
let nym`%#33` = builtins.nym`%` /* fn (x: Int, y: Int): Int | Bubble */;
@reach(\none) let mod1__0(n__0: Int, d__0: Int): Int | Bubble {
let return__0: Int;
@fail var fail#0: Boolean;
if (nym`==#32`(d__0, 0)) {
return failure;
} else {
return__0 = hs (fail#0, nym`%#33`(n__0, d__0));
if (fail#0) {
return failure;
}
}
return return__0;
}
@reach(\none) let mod2__0(n__1: Int, d__1: Int): Int | Bubble {
let return__1: Int;
@fail var fail#1: Boolean;
if (! nym`==#32`(d__1, 0)) {
return__1 = hs (fail#1, nym`%#33`(n__1, d__1));
if (fail#1) {
return failure;
}
} else {
return failure;
}
return return__1;
}
@reach(\none) let mod3__0(n__2: Int, d__2: Int): Int | Bubble {
fn__0: {
@fail var fail#2: Boolean;
if (! nym`==#32`(d__2, 0)) {
return__2 = hs (fail#2, nym`%#33`(n__2, d__2));
if (fail#2) {
return failure;
}
break fn__0;
}
return failure;
}
}
So it looks like the problem is in TmpL, specifically, the return variable is assigned, but it's not declared and no return return__2
statement is appended.
Ok, I've narrowed the problem down to CanControlFlowLeave
A Minimal Case
Lets say we wanted to write a
mod
function that checks for errors.Working Case 1
This is the code that does that.
Working Case 2
We can invert the if by inverting the condition and swapping the true/then case and false/else case.
Broken Case
We use the fact that the true/then case always returns (or calls
bubble()
in theory) to make it a guard-like statment. Note that an actual guard statement would check for bad values and then fail; We not check for good values and then succeed.Either way here's the new code.
Generated Code
Here's the python output (all comments are added after compilation to show the issue).
The Issue
This code now shows the bug on all backends; This has to be an issue with our Intermediate representation, TmpL.
My Solution
My proposal is to stop using a break after the return variable assignment and instead to use a return after declaring the return varaible. This helps in several ways.
let x; ...; x = ...;
returns
you have.Other Things
Fix the control flow around if. I bet this is still a good idea if my solution is adopted.