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

Compiler error when using a string generated with the `fmt""` macro twice in a template. #20381

Closed jesvedberg closed 1 year ago

jesvedberg commented 2 years ago

What happened?

If I pass a string generated using the fmt"" macro from strformat to a template that accepts a string, and then use that string twice in the template, I get a C compiler error. Here's a minimal example:

import strformat

template myTemplate(s: string) =
  echo s
  echo s

proc main() =
  myTemplate fmt"hello"

main()

When trying to compile this, I get an error message from the C compiler step shown below. If I don't use the fmt"" macro, this compiles as expected, and if I remove the the main() function and just call myTemplate globally it also compiles. Furthermore, the issue remains if I change the template argument type to typed, but it disappears if I change it to untyped.

Nim Version

Tested with 1.6.6 and 1.2.0 with the same result.

Current Standard Output Logs

Hint: used config file '/home/XXX/.choosenim/toolchains/nim-1.6.6/config/nim.cfg' [Conf]
Hint: used config file '/home/XXX/.choosenim/toolchains/nim-1.6.6/config/config.nims' [Conf]
..........................................................................
CC: template_test.nim
/home/XXX/.cache/nim/template_test_d/@mtemplate_test.nim.c: In function ‘main__template95test_4’:
/home/XXX/.cache/nim/template_test_d/@mtemplate_test.nim.c:118:24: error: redeclaration of ‘fmtRes’ with no linkage
  118 |         NimStringDesc* fmtRes;
      |                        ^~~~~~
/home/XXX/.cache/nim/template_test_d/@mtemplate_test.nim.c:116:24: note: previous declaration of ‘fmtRes’ with type ‘NimStringDesc *’
  116 |         NimStringDesc* fmtRes;
      |                        ^~~~~~
Error: execution of an external compiler program 'gcc -c  -w -fmax-errors=3   -I/home/XXX/.choosenim/toolchains/nim-1.6.6/lib -I/home/XXX/projects/nim -o /home/XXX/.cache/nim/template_test_d/@mtemplate_test.nim.c.o /home/XXX/.cache/nim/template_test_d/@mtemplate_test.nim.c' failed with exit code: 1

Expected Standard Output Logs

hello
hello

Possible Solution

I have no solution, but the issue can be worked around by either using specifying the template argument as untyped or by using a temporary variable in the template:

template myTemplate(s: string) =
  let s2 = s
  echo s2
  echo s2

Additional Information

As far as I can tell, this is a different issue than the limitation with strformat discussed here: https://nim-lang.org/docs/strformat.html#limitations

metagn commented 2 years ago

"Simpler" case:

import macros

macro foo() =
  let symName = genSym(nskLet, "tmp")
  result = newLetStmt(symName, newLit(123))

template myTemplate(x: typed): untyped =
  x
  x

proc main =
  myTemplate(foo())

main()
metagn commented 1 year ago

Workaround is wrap result of macro with block, which #21761 does for strformat and should close this issue.

The general case I posted above is still an issue but it's a separate priority and might not have an easy fix (again a workaround exists).