Can NOINLINE fail to prevent inlining?
Right, for your use case OPAQUE should be no less “fragile” than NOINLINE.
The goal of OPAQUE is: that every call of f generates, well, a call of f, not of some name-mangled variant.
So the property it provides is something only GHC API users and those trying to create minimal reproducers for GHC bugs, the people interested in the “syntax” of the intermediate Core representation, should really care about.
For everyone else, there should be no difference in the “semantics” of the intermediate Core representation between NOINLINE and OPAQUE, except that OPAQUE is likely to give you worse performance when GHC optimizations are turned on. Precisely because OPAQUE prevents certain optimizations that NOINLINE does allow.
I would even go as far as saying that I would consider it a GHC bug in the treatment of NOINLINE when the semantics of your code are as intended when you use OPAQUE, but not when you use NOINLINE; even in the face of unsafePerformIO.
Discussion in the ATmosphere