temperlang / temper

2 stars 0 forks source link

Bubbles create constrained init blocks #110

Open tjpalmer opened 3 months ago

tjpalmer commented 3 months ago

TLDR: We need to improve handling of top-level bubbling operations for catch handling, but we especially need to update how we handle them for if handling. Our current nested if/else blocks are hard for prod vs test code splitting if not other issues.

Also, note that splitting generic functions into separate decl and value fails on some backends, like how this is illegal Java:

static final BiConsumer<List<T__22>, Consumer<T__22>> forEach__89;

A general issue in #104 is how we build nested logic from multiple bubble handlings. For example, from this input:

let fuji = new Apple().maybe();
let gala = new Apple().maybe();
export class Apple {
  public maybe(): Apple | Bubble { this }
}

We can get trailing init blocks like in this:

@fail var fail#0: Boolean;
@fail var fail#1: Boolean;
class Apple / Apple {
  let maybe__0(this = this__0, this__0: Apple): Apple | Bubble {
    return /* this */ this__0;
  }
  constructor__0(this = this__1, this__1: Apple) {
    return;
  }
}
let fuji__0: Apple;
module init {
  moduleExit#0: {
    fuji__0 = hs (fail#0, /*new*/ Apple().maybe());
    if (! fail#0) {
      let gala__0: Apple;
      gala__0 = hs (fail#1, /*new*/ Apple().maybe());
      if (! fail#1) {
        break moduleExit#0;
      }
    }
    abortLoad;
  }
}

Anything that gets sorted to after a possible bubble at the top level gets stuck into such if/else nesting in a common init block. But it seems like we ought to be able to say things like this:

let fuji__0: Apple;
module init {
  fuji__0 = hs (fail#0, /*new*/ Apple().maybe());
  if (fail#0) {
    abortLoad;
  }
}

let gala__0: Apple;
module init {
  gala__0 = hs (fail#1, /*new*/ Apple().maybe());
  if (fail#1) {
    abortLoad;
  }
}

Maybe that's awkward to construct, but it seems like it would give use more flexibility in laying things out at top levels. (Even at nested scopes, seems like breaks ought to be usable for flattening, but that's outside the main concern of this issue.)

tjpalmer commented 3 months ago

We do seem to get tmpl like this when using catch bubble branch strategy:

let fuji__4: Apple;
module init {
  fuji__4 = /*new*/ Apple().maybe();
}
let gala__5: Apple;
module init {
  gala__5 = /*new*/ Apple().maybe();
}

But I'm still afraid that we're limiting our reordering of top levels. I don't know how to dig up easy examples.

tjpalmer commented 3 months ago

Example of a problem. In combineDeclarationsOnto, there's before and after a try block:

stmts = {ArrayList@5760}  size = 87
 0 = {PreTranslated$TreeWrapper@5765} TreeWrapper((Decl (L nym`-work/src///`.Color) (V \typeDecl) (V Color) (V \stay) (S) (V \ssa) (V void)))
...
 25 = {PreTranslated$TreeWrapper@5808} TreeWrapper((Decl (L oklabExpectedResults__91) (V \ssa) (V void) (V \reach) (V \test)))
...
 85 = {PreTranslated$TreeWrapper@5868} TreeWrapper((Decl (L oklabToSrgbLinear0__94) (V \ssa) (V void)))
 86 = {PreTranslated$Try@5869} Try(pos=-work/src//oklab.temper.md+1388-1545, tried=Block(pos=-work/src//oklab.temper.md+368-3815, elements=[TreeWrapper((Call (V nym`=`) (L oklabToSrgbLinear0__94) (Call (V hs) (L fail#544) (Call (V do_call_transpose) (Call (V new) (V Matrix) (V 3) (Call (V list) (V 1.0) (V 0.3963377774) (V 0.2158037573) (V 1.0) (V -0.1055613458) (V -0.0638541728) (V 1.0) (V -0.0894841775) (V -1.291485548))))))), TreeWrapper((Decl (L oklabToSrgbLinear1__95) (V \ssa) (V void))), TreeWrapper((Call (V nym`=`) (L oklabToSrgbLinear1__95) (Call (V hs) (L fail#546) (Call (V do_call_transpose) (Call (V new) (V Matrix) (V 3) (Call (V list) (V 4.0767416621) (V -3.3077115913) (V 0.2309699292) (V -1.2684380046) (V 2.6097574011) (V -0.3413193965) (V -0.0041960863) (V -0.7034186147) (V 1.707614701))))))), TreeWrapper((Call (V nym`=`) (L oklabToSrgbLinear__90) (Fun (Decl (L lab__288) (V \type) (V Matrix) (V \word) (V \lab)) (V \returnDecl) (Decl (L return__71) (V \type) (V Matrix) (V \ssa) (V void)) (V \returnedFrom
  pos = {Position@6024} -work/src//oklab.temper.md+1388-1545
  tried = {PreTranslated$Block@6025} Block(pos=-work/src//oklab.temper.md+368-3815, elements=[TreeWrapper((Call (V nym`=`) (L oklabToSrgbLinear0__94) (Call (V hs) (L fail#544) (Call (V do_call_transpose) (Call (V new) (V Matrix) (V 3) (Call (V list) (V 1.0) (V 0.3963377774) (V 0.2158037573) (V 1.0) (V -0.1055613458) (V -0.0638541728) (V 1.0) (V -0.0894841775) (V -1.291485548))))))), TreeWrapper((Decl (L oklabToSrgbLinear1__95) (V \ssa) (V void))), TreeWrapper((Call (V nym`=`) (L oklabToSrgbLinear1__95) (Call (V hs) (L fail#546) (Call (V do_call_transpose) (Call (V new) (V Matrix) (V 3) (Call (V list) (V 4.0767416621) (V -3.3077115913) (V 0.2309699292) (V -1.2684380046) (V 2.6097574011) (V -0.3413193965) (V -0.0041960863) (V -0.7034186147) (V 1.707614701))))))), TreeWrapper((Call (V nym`=`) (L oklabToSrgbLinear__90) (Fun (Decl (L lab__288) (V \type) (V Matrix) (V \word) (V \lab)) (V \returnDecl) (Decl (L return__71) (V \type) (V Matrix) (V \ssa) (V void)) (V \returnedFrom) (V true) (V \word) (V \oklabToSrgbLinear) (V \stay
   pos = {Position@6834} -work/src//oklab.temper.md+368-3815
   elements = {ArrayList@6835}  size = 77
    0 = {PreTranslated$TreeWrapper@6838} TreeWrapper((Call (V nym`=`) (L oklabToSrgbLinear0__94) (Call (V hs) (L fail#544) (Call (V do_call_transpose) (Call (V new) (V Matrix) (V 3) (Call (V list) (V 1.0) (V 0.3963377774) (V 0.2158037573) (V 1.0) (V -0.1055613458) (V -0.0638541728) (V 1.0) (V -0.0894841775) (V -1.291485548)))))))
...
    71 = {PreTranslated$TreeWrapper@6909} TreeWrapper((Call (V nym`=`) (L oklabExpectedResults__91) (Fun (V \returnDecl) (Decl (L return__72) (V \type) (V Color) (V \ssa) (V void)) (V \returnedFrom) (V true) (V \word) (V \oklabExpectedResults) (V \stay) (S) (Block (ComplexFlow ([Entry] -- [void] -- [void] -- [void] -- [void] -- [void] -- [void] -- [void] -- [void] -- [return__72 = getStat⋯.0, 0.55, 0.0, -0.2))] -- [void] -- [void] -- [void] -- 0[Exit])) (V void) (V void) (V void) (V void) (V void) (V void) (V void) (V void) (Call (V nym`=`) (L return__72) (Call (Call (V getStatic) (V Color) (V \of)) (Call (V getStatic) (V Space) (V \oklab)) (Call (V list) (V 0.51829) (V -0.13991) (V 0.10737) (V 0.0) (V 0.0) (V 0.0) (V 1.0) (V 0.0) (V 0.0) (V 0.5) (V 0.05) (V 0.0) (V 0.55) (V 0.0) (V -0.2)))) (V void) (V void) (V void) (V false) (V false) (V false)))))
...
    76 = {PreTranslated$TreeWrapper@6914} TreeWrapper((Call (V nym`=`) (L nym`-work/src///`.rgbUnitToString) (Fun (Decl (L rgb__324) (V \type) (V Matrix) (V \word) (V \rgb)) (V \returnDecl) (Decl (L return__75) (V \type) (V List<String>) (V \ssa) (V void)) (V \returnedFrom) (V true) (V \word) (V \rgbUnitToString) (V \stay) (S) (Block (ComplexFlow ([Entry] -- [void] -- [var t#1485] -- [void] -- [void] -- [void] -- [void] -- [void] -- [void] -- [void] -- [void] -- [t#1485 = (fn unitToString)(rgb__324)] -- [let fn__1483] -- [fn__1483 = (@stay fn⋯= cat("#", it__326)})] -- [void] -- [return__75 = do_call⋯map(t#1485, fn__1483)] -- [void] -- [void] -- [void] -- 0[Exit])) (V void) (Decl (L t#1485) (V \var) (V void) (V \ssa) (V void)) (V void) (V void) (V void) (V void) (V void) (V void) (V void) (V void) (Call (V nym`=`) (L t#1485) (Call (V fn unitToString) (R rgb__324))) (Decl (L fn__1483) (V \ssa) (V void)) (Call (V nym`=`) (L fn__1483) (Fun (Decl (L it__326) (V \word) (V \it)) (V \returnDecl) (Decl (L return__327) (V \type) (V Stri
   fixedElements {java.util.List} 
   children {kotlin.sequences.Sequence} 
   roots {kotlin.sequences.Sequence} 
  recover = {PreTranslated$Goal@6026} Goal(goal:*fail*)

Because oklabExpectedResults__91 has a decl in one section and the assignment in the try, it doesn't get merged. Either the decl or the assignment could move in or out of the try block in this case, but they're in separate top levels.