Skip to content

Generated enum case code may trigger -Wmaybe-uninitialized #22246

@etan-status

Description

@etan-status

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 $i

test.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.a

Nim 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-uninitialized to suppress the warning.
  • Disable -Werror to 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions