-
- Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Description
When linking static Nim libraries from C with LTO, -Wmaybe-uninitialized GCC warnings may be emitted when linked Nim code uses a case statement to assign to a local variable, and then using it later.
Example:
test.nim
type E = enum a, b proc createA(): ptr E {.cdecl, exportc, dynlib, raises: [].} = result = E.create() result[] = a proc foo(e: ptr E) {.cdecl, exportc, dynlib, raises: [].} = let i = case e[] of a: 5 of b: 2 echo $itest.c
void NimMain(void); void *createA(); void foo(void *e); int main() { NimMain(); foo(createA()); }nim c -d:release --app:staticlib --noMain '--passC:-flto=auto' -o:test.a test.nim gcc --std=c17 -Wall -Wextra -pedantic -Werror -pedantic-errors -flto -o test test.c test.aNim Version
Nim Compiler Version 1.6.14 [Linux: amd64] Compiled at 2023-07-10 Copyright (c) 2006-2023 by Andreas Rumpf git hash: 71ba2e7f3c5815d956b1ae0341b0743242b8fec6 active boot switches: -d:release # gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.3.0-1ubuntu1~22.04.1' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-11-aYxV0E/gcc-11-11.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-aYxV0E/gcc-11-11.3.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2 Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04.1) Current Output
/root/.cache/nim/test_r/@mtest.nim.c: In function 'foo': /root/.cache/nim/test_r/@mvendor@snimbus-build-system@svendor@sNim@slib@ssystem@sdollars.nim.c:38:9: error: 'i' may be used uninitialized in this function [-Werror=maybe-uninitialized] 38 | addInt__stdZprivateZdigitsutils_167((&result), x); | ^ /root/.cache/nim/test_r/@mtest.nim.c:79:12: note: 'i' was declared here 79 | NI i; | ^ lto1: all warnings being treated as errors lto-wrapper: fatal error: gcc returned 1 exit status compilation terminated. /usr/bin/ld: error: lto-wrapper failed collect2: error: ld returned 1 exit status Expected Output
Successful compilation, and `5` when running ./test Possible Solution
https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wmaybe-uninitialized
GCC documentation states that a default assert(0) case should be emitted to indicate to the compiler that the switch is exhaustive.
These warnings are made optional because GCC may not be able to determine when the code is correct in spite of appearing to have an error. Here is one example of how this can happen:
{ int x; switch (y) { case 1: x = 1; break; case 2: x = 4; break; case 3: x = 5; } foo (x); }If the value of y is always 1, 2 or 3, then x is always initialized, but GCC doesn’t know this. To suppress the warning, you need to provide a default case with assert(0) or similar code.
The generated Nim code is exactly this pattern, but doesn't have a default case with an assert(0). Therefore, GCC emits a -Wmaybe-uninitialized warning. Note that the assert(0) will be optimized away later in release mode, so just serves as a hint to GCC.
N_LIB_EXPORT N_CDECL(void, foo)(tyEnum_E__TG9aP9cVRakngfOFri0rZ4ig* e) { NI i; tyArray__nHXaesL0DJZHyVS07ARPRA T3_; switch ((*e)) { case ((tyEnum_E__TG9aP9cVRakngfOFri0rZ4ig) 0): { i = ((NI) 5); } break; case ((tyEnum_E__TG9aP9cVRakngfOFri0rZ4ig) 1): { i = ((NI) 2); } break; } nimZeroMem((void*)T3_, sizeof(tyArray__nHXaesL0DJZHyVS07ARPRA)); T3_[0] = dollar___systemZdollars_3(i); echoBinSafe(T3_, 1); }Additional Information
Workarounds:
- Compile the C program with
-Wno-maybe-uninitializedto suppress the warning. - Disable
-Werrorto convert the error to a warning. - Use Clang instead of GCC, but that requires also compiling the library with Clang, as LTO information is compiler specific.