Hacker News new | past | comments | ask | show | jobs | submit login

   if (!x) std::abort();
   if (!x) return;
In this case, the compiler should warn that the second statement will never be executed, instead of just silently removing it.



   int *x = libX_foo();
   if (!x) {
      return;
   }
   ...
libX_foo from libX gets at some point updated to abort if the return value would be null. After interprocedural analysis (possibly during LTO) the compiler infers that the if statement is redundant.

Should the compiler complain? Should you remove the check?

Consider that libX_foo returning not-null might not be part of the contract and just an implementation detail of this version.


> Should the compiler complain? Should you remove the check?

Yes and yes.

> Consider that libX_foo returning not-null might not be part of the contract and just an implementation detail of this version.

How is it an “implementation detail” whether a procedure can return null? That's always an important part of its interface.


> How is it an “implementation detail” whether a procedure can return null? That's always an important part of its interface.

In gpderetta's example, the interface contract for that function says "it can return null" (which is why the calling code has to check for null). The implementation for this particular version of the libX code, however, never returns null. That is, when the calling code is linked together with that particular version of the libX interface, and the compiler can see both the caller and the implementation (due to link-time optimization or similar), it can remove the null check in the caller. But it shouldn't complain, because the null check is correct, and will be used when the program is linked with a different version of the libX code which happens to be able to return null.

For a more concrete example: libX_foo is a function which does some calculations, and allocates temporary memory for these calculations, and this temporary allocation can fail. A later version of libX_foo changes the code so it no longer needs a temporary memory allocation, so it no longer can fail.

And LTO is not even necessary. It could be an inline function defined in a header coming from libX (this kind of thing is very common in C++ with template-heavy code). The program still cannot assume a particular version of libX, so it still needs the null check, even though in some versions of libX the compiler will remove it.


Thanks for elaborating on this.

I mentioned LTO because compilation units were seen in the past as safe optimization barriers.


The contract is that libX_foo can return null. But a specific implementation might not. Now you need to remove the caller side check to shut up the compiler which will leave you exposed to a future update making full use of the contract.

Also consider code that call libX_foo via a pointer. After specialization the compiler might see that the check is redundant, but you can't remove the check because the function might still be called with other function pointers making full use of the contract.


> The contract is that libX_foo can return null.

I'd expect any reasonable library to say “libX_foo returns null if [something happens]”. What use is there in a procedure that can just return null whenever it feels like it?


It returns null when it fails to do its task for some reason. It is not unreasonable for the condition for that failure to be complex enough or change over time so it doesn't make sense to spell it out in the interface contract.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: