nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.58k stars 1.47k forks source link

Exporting generic methods to C causes name clashes #13390

Open deech opened 4 years ago

deech commented 4 years ago

If you try to compile the following file:

proc id*[T](t:T):T {.exportc,dynlib,cdecl.} = t

discard id(5)
discard id("hello world")

with the settings:

--app:lib
--header
--noMain

you get:

In file included from test.nim.c:9:
test.c:47:38: error: conflicting types for ‘id’
   47 | N_LIB_EXPORT N_CDECL(NimStringDesc*, id)(NimStringDesc* t);
      |                                      ^~
/home/deech/Nim/Nim/lib/nimbase.h:194:44: note: in definition of macro ‘N_CDECL’
  194 | #    define N_CDECL(rettype, name) rettype name
      |                                            ^~~~
test.nim.c:43:26: note: previous declaration of ‘id’ was here
   43 | N_LIB_EXPORT N_CDECL(NI, id)(NI t);
      |                          ^~
/home/deech/Nim/Nim/lib/nimbase.h:194:44: note: in definition of macro ‘N_CDECL’
  194 | #    define N_CDECL(rettype, name) rettype name
      |                                            ^~~~
test.nim.c:90:38: error: conflicting types for ‘id’
   90 | N_LIB_EXPORT N_CDECL(NimStringDesc*, id)(NimStringDesc* t) {
      |                                      ^~
/home/deech/Nim/Nim/lib/nimbase.h:194:44: note: in definition of macro ‘N_CDECL’
  194 | #    define N_CDECL(rettype, name) rettype name
      |                                            ^~~~
test.nim.c:81:26: note: previous definition of ‘id’ was here
   81 | N_LIB_EXPORT N_CDECL(NI, id)(NI t) {
      |                          ^~
/home/deech/Nim/Nim/lib/nimbase.h:194:44: note: in definition of macro ‘N_CDECL’
  194 | #    define N_CDECL(rettype, name) rettype name
      |                                            ^~~~

This is happening because id gets generated twice, once for the int instantiation and once for the string. You can see this in the generated header:

...
N_LIB_IMPORT N_CDECL(NI, id)(NI t);
N_LIB_IMPORT N_CDECL(NimStringDesc*, id)(NimStringDesc* t);
...

It should refuse to compile a generic function with exportc and cdecl or should name the exported instantiations predictibly like id_int or id_string.

Nim version:

Nim Compiler Version 1.1.1 [Linux: amd64]
Compiled at 2020-02-08
Copyright (c) 2006-2019 by Andreas Rumpf

git hash: e4415422fe2b615a6dfe01bb7136ec41ef429108
active boot switches: -d:release
Araq commented 4 years ago

shrug And for nim cpp it can work out...

deech commented 4 years ago

Just checked, it does the same thing if you set the backend to cpp and change exportc to exportcpp.

Araq commented 4 years ago

It doesn't matter, if you use exportc or emit or header there is a limit to what we can check before we hit the C compiler / linker. Eventually we have to trust that the programmer actually understands both C(++) and Nim for getting them work well together.

deech commented 4 years ago

Sure I understand, but warning or erroring when trying to export any type signature that has a free type variable seems like it could save someone hours of scouring a big pile of generated C/C++. It's an easy mistake to make and a hard one to detect especially since if the generic is only instantiated once everything works fine.