charon icon indicating copy to clipboard operation
charon copied to clipboard

Bug: duplication of basic-blocks for test conditions of the form e1 && e2 (control-flow reconstruction issue)

Open msprotz opened this issue 4 months ago • 4 comments

This:

fn h(x: &str) {}

fn main() {
    {
        if true && true {
            h("foo")
        } else { 
            h("bar")
        }
    }
}

gets pretty-printed as:

// Full name: test3::h
fn h<'_0>(@1: &'_0 (Str))
{
    let @0: (); // return
    let x@1: &'_ (Str); // arg #1

    @0 := ()
    @0 := ()
    return
}

// Full name: test3::main
fn main()
{
    let @0: (); // return
    let @1: bool; // anonymous local
    let @2: bool; // anonymous local
    let @3: &'_ (Str); // anonymous local
    let @4: &'_ (Str); // anonymous local
    let @5: &'_ (Str); // anonymous local
    let @6: &'_ (Str); // anonymous local

    storage_live(@3)
    storage_live(@4)
    storage_live(@5)
    storage_live(@6)
    storage_live(@1)
    @1 := const (true)
    if move (@1) {
        storage_live(@2)
        @2 := const (true)
        if move (@2) {
            storage_live(@3)
            storage_live(@4)
            @4 := const ("foo")
            @3 := &*(@4)
            @0 := h<'_>(move (@3))
            storage_dead(@4)
            storage_dead(@3)
        }
        else {
            storage_live(@5)
            storage_live(@6)
            @6 := const ("bar")
            @5 := &*(@6)
            @0 := h<'_>(move (@5))
            storage_dead(@6)
            storage_dead(@5)
        }
    }
    else {
        storage_live(@5)
        storage_live(@6)
        @6 := const ("bar")
        @5 := &*(@6)
        @0 := h<'_>(move (@5))
        storage_dead(@6)
        storage_dead(@5)
    }
    storage_dead(@2)
    storage_dead(@1)
    @0 := ()
    return
}

Notice how the const("bar") appears twice, meaning that the else-branch is duplicated. The desired outcome is for no such duplication to occur.

This is throwing Eurydice off the rails in a non-recoverable way. I'll elaborate once I file an issue on the Eurydice side.

msprotz avatar Aug 13 '25 20:08 msprotz

Oh interesting, it seems the boolean condition just isn't computed and it's all done via control flow instead. That's not easy to reconstruct 🤔

Nadrieril avatar Sep 08 '25 16:09 Nadrieril

Standard desugaring of lazy boolean operators to make sure the right hand side isn't eagerly evaluated?

if (e1 && e2) {
  e3
} else {
  e4

becomes

if (e1) {
  if (e2) {
    e3
  } else {
    e4
  }
} else {
  e4
}

I guess there could be a "peephole" optimization pattern for this that recognizes exactly this desugaring?

msprotz avatar Sep 08 '25 16:09 msprotz

Yeah, I want to emit something like:

if e1 {
  b = e2;
} else {
  b = false;
}
if b {
  e3
} else {
  e4
}

We can probably recognize cases like this during control-flow reconstruction.

Nadrieril avatar Sep 09 '25 09:09 Nadrieril

That would be perfect and I think would solve my use-case.

protz avatar Sep 11 '25 04:09 protz