ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
35.02k stars 2.56k forks source link

Proposal: make `if` with a single statement that uses `defer` a compiler error #18911

Open Jarred-Sumner opened 9 months ago

Jarred-Sumner commented 9 months ago

The following is an easy mistake for developers new to Zig:

if (condition) {
  // this runs immediately! not after now()
  defer later();
}

now();

Someone new to Zig would think "okay, later() is going to run after now().", but actually it runs immediately.

Is there ever a case where a single-statement if with defer is not a bug?

If there are no cases, could this be a compiler error?

For example, I am not suggesting the following:

// these are all fine! 
if (condition) {
  defer later();
  anotherFn();
}
if (condition) {
  anotherFn();
  // should this be fine? i'm not sure
  defer later();
}
defer if (true) later();
defer {
  if (true) later();
 }
defer {
    if (true) {
      later();
    }
 }

Motivating example: https://github.com/oven-sh/bun/blob/f1d2fcca16f31b612e944ebe8f7de7abc8c94347/src/bun.js/javascript.zig#L2768-L2770

I am less certain of the behavior with errdefer

rohlem commented 9 months ago

I agree that a block/body with only defer will generally be an error, however, commenting-out code can also turn valid and intentional code into this shape.

if (condition) {
  defer z(n);
  defer a(n);
  b(&n);
}
// comment out b, would now be a compile error
if (condition) {
  defer z(n);
  defer a(n);
  //b(&n);
}
// to make it compile again
if (condition) {
  defer z(n);
  a(n); //defer when we reactivate a statement below
  //b(&n);
}

This is a bit similar to some of the _ = &x; statements that need to be introduced for unused/unmodified variable errors, so there's definitely precedent to go ahead with it regardless (and I'm not personally opposed either).

I'm wondering whether it would make sense to additionally introduce a sort of "void statement" to specifically silence / work around this new "immediate-defer" compile error. Could be _ = _;, or even a named builtin @confirmDefers(); which would be more obvious.

I am less certain of the behavior with errdefer

IMO if we can settle on a canonical workaround, like @confirmDefers(), then the compile error would be less intrusive to work with and we could include errdefer without issue.

paperdave commented 9 months ago

i don't think this should be specific to if statements, but just blocks that include defer as the only item.

rohlem commented 9 months ago

@paperdave It could in fact be generalized to any sequence of defer/errdefer at the end of a block (i.e. that aren't followed by any other statement), no matter what came before.

Cloudef commented 9 months ago

Perhaps also the error could give a hint how to do what they want

defer if (cond) later();