clangir icon indicating copy to clipboard operation
clangir copied to clipboard

Normal cleanup is incorrect for objects created in a loop condition expression

Open andykaylor opened this issue 5 months ago • 0 comments

If I create an object that requires cleanup in the condition of a for-loop, the cleanup is created in the wrong scope.

#include <iostream>

struct Struk {
  Struk(int i) : i(i) { std::cout << "Struk(" << i << ")\n"; }
  ~Struk() { std::cout << "~Struk()\n"; }
  operator bool() { return i < 10; }

private:
  int i;
};

int main() {
  for (int i = 0; Struk a{i}; ++i) {

  }
}

When compiled with -fno-exceptions is represented as:

cir.func dso_local @main() -> !s32i {
  %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
  cir.scope {
    %2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
    %3 = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>, ["a", init] {alignment = 4 : i64}
    %4 = cir.const #cir.int<0> : !s32i
    cir.store align(4) %4, %2 : !s32i, !cir.ptr<!s32i>
    cir.for : cond {
      %5 = cir.load align(4) %2 : !cir.ptr<!s32i>, !s32i
      cir.call @_ZN5StrukC2Ei(%3, %5) : (!cir.ptr<!rec_Struk>, !s32i) -> ()
      %6 = cir.call @_ZN5StrukcvbEv(%3) : (!cir.ptr<!rec_Struk>) -> !cir.bool
      cir.condition(%6)
    } body {
      cir.yield
    } step {
      %5 = cir.load align(4) %2 : !cir.ptr<!s32i>, !s32i
      %6 = cir.unary(inc, %5) nsw : !s32i, !s32i
      cir.store align(4) %6, %2 : !s32i, !cir.ptr<!s32i>
      cir.yield
    }
    cir.call @_ZN5StrukD2Ev(%3) : (!cir.ptr<!rec_Struk>) -> ()
  }
  %1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
  cir.return %1 : !s32i
}

The Struk constructor is correctly called each time the condition is evaluated, but the destructor is only called once, at the end of the loop.

https://godbolt.org/z/sbW7hEjGa

An equivalent problem also occurs for a variable declared in the condition of a while loop.

https://godbolt.org/z/Pjeandq5h

andykaylor avatar Jul 21 '25 22:07 andykaylor