Option to suppress errors related to unsupported features
From what I've gathered so far, it seems like unsupported functionality is guarded by emission of preprocessor errors at the point of #including a header that represents such functionality. Maybe there's something I've missed, but this seems somewhat heavy handed and difficult to work around. Presumably in most cases, including a header alone would not cause issues so long as certain symbols are not referenced?
My immediate issue relates to trying to build code for WASI which uses C++20 modules (a niche combo admittedly). Since the standard library module will just include a bunch of system headers, the build fails right away:
In file included from C:\WASM\wasi-sdk-25.0-x86_64-windows\share\wasi-sysroot\share\libc++\v1\std.cppm:52:
In file included from C:\WASM\wasi-sdk-25.0-x86_64-windows\bin\../share/wasi-sysroot/include/wasm32-wasi/c++/v1\csetjmp:37:
C:\WASM\wasi-sdk-25.0-x86_64-windows\bin\../share/wasi-sysroot/include/wasm32-wasi\setjmp.h:13:2: error: Setjmp/longjmp support requires Exception handling support, which is [not yet standardized](https://github.com/WebAssembly/proposals?tab=readme-ov-file#phase-3---implementation-phase-cg--wg). To enable it, compile with `-mllvm -wasm-enable-sjlj` and use an engine that implements the Exception handling proposal.
13 | #error Setjmp/longjmp support requires Exception handling support, which is [not yet standardized](https://github.com/WebAssembly/proposals?tab=readme-ov-file#phase-3---implementation-phase-cg--wg). To enable it, compile with `-mllvm -wasm-enable-sjlj` and use an engine that implements the Exception handling proposal.
| ^
In file included from C:\WASM\wasi-sdk-25.0-x86_64-windows\share\wasi-sysroot\share\libc++\v1\std.cppm:53:
In file included from C:\WASM\wasi-sdk-25.0-x86_64-windows\bin\../share/wasi-sysroot/include/wasm32-wasi/c++/v1\csignal:46:
C:\WASM\wasi-sdk-25.0-x86_64-windows\bin\../share/wasi-sysroot/include/wasm32-wasi\signal.h:2:2: error: "wasm lacks signal support; to enable minimal signal emulation, compile with -D_WASI_EMULATED_SIGNAL and link with -lwasi-emulated-signal"
2 | #error "wasm lacks signal support; to enable minimal signal emulation, \
| ^
2 errors generated.
Is it feasible to allow for these errors to be conditionally disabled via macros (actually it seems for the signals there is a macro, but not for the exceptions), leaving it up to the user to navigate potential linker/runtime issues that could result?
This seems like the kind of thing @sbc100 has been for: it would certainly make it easier to compile more things. I'm pretty neutral and your "extra macro to just compile the thing" idea makes sense to me. @alexcrichton, @sunfishcode?
Yes, perhaps simply including the header should probably not be and error? But using / referencing missing symbols probably should continue to be a compiler/link failure.
Is the C++ module system going to want to unconditionally reference symbols that wasi-libc doesn't provide? Or does it have some way to make such things optional?
I find in situations like this there's not really a best answer and "someone always loses" in the sense that someone is going to get a bad error message they don't understand somewhere along the line. The current system punishes #include-ing files that you don't actually use the symbols from. Not having these guards at all (which I realize isn't being proposed but wanted to list our here for completeness) would punish users who are using the symbols with obscure compiler warnings ("but I included the header?!"). Adding an opt-in punishes those who need the opt-in with extra syntax to pass.
Personally I sort of feel that everything is a not-so-great option here so I'd defer to the side of something that's easiest to maintain which to me would be to remove #error guards but also remove the symbols that wasi-libc doesn't have. That would mean we punish users actually using these symbols with compiler errors about referring to symbols that don't exist, but I feel that it's the most flexible in terms of integrating upstream in projects since the headers exist and the contents are somewhat there and it's only if things are actually used does something break, but that's inevitable and it's just a question about how nice the error message is which we can in theory mitigate with documentation for wasi-{libc,sdk}
Is the C++ module system going to want to unconditionally reference symbols that wasi-libc doesn't provide? Or does it have some way to make such things optional?
I believe it should simply be re-exporting names and wouldn't cause any emission of symbol references itself, but I'll try to make some local modifications to the headers in question to see if I can verify this.
Doesn't re-exporting a symbol foo require that the symbol foo is actually defined? What about platforms that don't provide foo?
Doesn't re-exporting a symbol
foorequire that the symbolfoois actually defined? What about platforms that don't providefoo?
I don't believe so, no. Though I'm a little unsure of your precise meaning of symbol and defined. Certainly the named entity being exported needs to have been declared and accessible at the point it's exported. Other than that though, it really is just a case of making the name available to lookup for consumers of the module, so has no effect on symbols as I understand the term to mean as far as the linker is concerned.
FWIW, I commented out the #error line in WASI setjmp.h, and was able to build the std module, and then link and run my program without any apparent issue.
I'm out of my depth here so not qualified to give suggestions as to the best way to solve the issue, but I did a bit of digging in case it's useful. Here's an excerpt from the libc++ std module source:
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <codecvt>
#endif
#include <compare>
#include <complex>
#include <concepts>
#include <condition_variable>
#include <coroutine>
#include <csetjmp>
#include <csignal>
So you can see some things are guarded there; perhaps a change at this level is also a possibility?
Then later on we have:
export namespace std {
using std::jmp_buf _LIBCPP_USING_IF_EXISTS;
using std::longjmp _LIBCPP_USING_IF_EXISTS;
} // namespace std
So it's exporting a type and a function. So long as user code doesn't try to use the function, then the compiler won't emit a symbol reference for it just from this. The macro conditionally expands to a __using_if_exists__ attribute; that also looks somewhat relevant, but I don't know anything about it.
Anyway, to sum up, my feeling is that there's nothing really special about the std library module case. I think it's basically the exact same issue that could occur in any other context where someone targetting WASI pulls in some code which they don't have full control over, which in turn #includes certain system headers even though it (or the user's program) may not end up actually using the problematic features.
Anyway, to sum up, my feeling is that there's nothing really special about the std library module case. I think it's basically the exact same issue that could occur in any other context where someone targetting WASI pulls in some code which they don't have full control over, which in turn
#includes certain system headers even though it (or the user's program) may not end up actually using the problematic features.
Normally/historically, in portable software, references to libc functions (for example, setjmp) would be guard by some kind of HAVE_SETJMP macro. This does often also include headers too. HAVE_SETJMP_H, for example. However it sounds like the __using_if_exists__ macro takes care of that in this case, although the existence of the header appears to be mandatory.
I'd be OK with providing an empty header in this case, but it does seems a little odd.
Ah, the libcxx/include/__cxx03/csetjmp file does seem to be doing the right things and checking for __has_include(<setjmp.h>) first:
https://github.com/llvm/llvm-project/blob/a4e4527c4b44be9a88168c0a4758de58fd1a770d/libcxx/include/__cxx03/csetjmp#L35-L41
So perhaps we just need to remove the unconditional #error when the header is included.
Or we could simple remove the header completely.. that seems like it would also work for libc++
What if we moved the error message from a #error to a deprecated attribute, so that users still see the "use -mllvm -wasm-enable-sjlj`" message? It's not deprecation per se, but it would be a way to help users see "here's how to do this" rather than "this just doesn't work".
This error message isn't even accurate anymore:
error: Setjmp/longjmp support requires Exception handling support, which is not yet standardized. To enable it, compile with
-mllvm -wasm-enable-sjljand use an engine that implements the Exception handling proposal.
Exception Handling is Phase 5: https://github.com/WebAssembly/proposals/blob/main/finished-proposals.md
Why is clang still emitting this?