Hacker News new | ask | show | jobs
by mikeash 3186 days ago
"Can't" here means that your program is not well-formed otherwise, and the compiler assumes well-formedness.

I assume you don't like that, but I wonder if you'd apply that to other optimizations? For a random example:

  int x = 42;
  SomeFunc();
  printf("%d\n", x);
Should the compiler be allowed to hard-code 42 as the second parameter to printf, or should it always store 42 to the stack before calling SomeFunc(), then loading back out? SomeFunc might lightly smash the stack and change the value of x, after all.
1 comments

Hardcoding 42 as the parameter to printf here is far more defensible for several reasons. Here's one: the value actually is 42, and assuming that it continues to be 42 doesn't require the compiler to hallucinate any additional instructions outside this compilation unit.

There's a difference between assuming that a function like SomeFunc internally obeys the language semantics for the sake of code around its call site (this is the definition of modularity), and assuming that because the code around the call site "must" be "well-formed" this allows you to hallucinate whatever code you need to add elsewhere to retroactively make the call site "well-formed" (this is the definition of non-modularity).

What's the difference between assuming that a function you call will obey the language semantics, and assuming that the function that calls you will obey the language semantics? That's the only difference I can see.
> assuming that the function that calls you will obey the language semantics

That's not what I said.

What the compiler is doing in this NeverCalled example is observing: - that the code in the current compilation unit is not "well-formed", but - that the compilation unit can be "rescued" by some other module that could be linked in, if that other module did something specific, and therefore concluding that it should imagine that this other module exists and does this exact thing, despite the fact that such module is in fact entirely a hallucination.

This is very different from simply assuming that a thing that in fact exists really does implement its stated interface.

Here's a different example:

  #include <stdio.h>

  typedef int (*Function)();

  static Function Do;

  static int Boom() {
    return printf("<boom>\n");
  }

  void NeverCalled() {
    Do = Boom;
  }

  void MightDoSomething();

  int main() {
    printf("Do = %p\n", Do);
    MightDoSomething();
    return Do();
    printf("after Do\n");
  }
In this case, it is possible that MightDoSomething could call NeverCalled, and that's one way this module could rescued from not being "well-formed". Should the compiler assume that MightDoSomething calls NeverCalled at some point then? No, that's absurd. There's nothing about the "void()" function interface that obliges such a function to clean up after you if write code that dereferences a null pointer or divides by zero.

We trust that a random void() function won't smash the stack and overwrite local variables, because that's a reasonable interface for a function to have. That's composable. That's different from expecting it to do "whatever it takes" to fix local undefined behaviour.

When you say "its stated interface," are you referring purely to the prototype, or are you referring to documented behaviors, or what? Because it seems reasonable to me for a function with no parameters to have prerequisites before you call it, and it seems unreasonable to say that it must be valid to call a function with no parameters in any and all circumstances.