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.59k stars 1.47k forks source link

Templates bad expansions in arguments #24421

Closed juancarlospaco closed 3 days ago

juancarlospaco commented 3 days ago

Description

Templates expand to rvalue=rvalue (right-hand side literal values) arguments when the template argument and the function argument have the same name.

Same happens inside a main() function.

Alternative can be change identifier names like body = bodi in arguments, but its weird and don't change the fact that the compiler is using literal values as identifier names in expansions.

func f(a: string) = discard       # Expands to:
template t(a: string) = f(a = a)  # f("BUG" = "BUG")
t(a = "BUG")                      # Should be:
                                  # f(a = "BUG")

Nim Version

2.2.1

Current Output

temp.nim(3, 7) Error: identifier expected, but found '"BUG"'

Expected Output

Compile Ok, or give some kind of ambiguous identifier error?

Known Workarounds

Change the name of the arguments, but you have to edit the whole code.

I found this because I have templates that use body=body for HTTP code, and the expandMacros shows code like f( "BUG" = "BUG" ), but should be like f( body = "BUG" ), then complains Error: identifier expected, but found '"BUG"'.

Additional Information

See Bisect below...

juancarlospaco commented 3 days ago

!nim c

func f(a: string) = discard
template t(a: string) = f(a = a)
t(a = "BUG")
github-actions[bot] commented 3 days ago
:penguin: Linux bisect by @juancarlospaco (collaborator)
devel :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:03
  • Finished 2024-11-09T00:47:04
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
stable :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:07
  • Finished 2024-11-09T00:47:08
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
2.0.10 :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:11
  • Finished 2024-11-09T00:47:11
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
2.0.0 :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:15
  • Finished 2024-11-09T00:47:15
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
1.6.20 :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:18
  • Finished 2024-11-09T00:47:19
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
1.4.8 :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:22
  • Finished 2024-11-09T00:47:22
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
1.2.18 :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:25
  • Finished 2024-11-09T00:47:25
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
1.0.10 :-1: FAIL

Output

``` ```

IR

Compiled filesize 0 (0 bytes) ```cpp ```

Stats

  • Started 2024-11-09T00:47:28
  • Finished 2024-11-09T00:47:28
  • Duration

AST

```nim nnkStmtList.newTree( nnkFuncDef.newTree( newIdentNode("f"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkDiscardStmt.newTree( newEmptyNode() ) ) ), nnkTemplateDef.newTree( newIdentNode("t"), newEmptyNode(), newEmptyNode(), nnkFormalParams.newTree( newEmptyNode(), nnkIdentDefs.newTree( newIdentNode("a"), newIdentNode("string"), newEmptyNode() ) ), newEmptyNode(), newEmptyNode(), nnkStmtList.newTree( nnkCall.newTree( newIdentNode("f"), nnkExprEqExpr.newTree( newIdentNode("a"), newIdentNode("a") ) ) ) ), nnkCall.newTree( newIdentNode("t"), nnkExprEqExpr.newTree( newIdentNode("a"), newLit("BUG") ) ) ) ```
Stats
  • GCC 11.4.0
  • Clang 14.0.0
  • NodeJS 20.4
  • Created 2024-11-09T00:46:34Z
  • Comments 1
  • Commands nim c --run -d:nimDebug -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim

:robot: Bug found in 27 mins bisecting 8 commits at 0 commits per second

beef331 commented 3 days ago

This is expected given that any usage of an identifier expands to the parameter.

metagn commented 3 days ago

7811

juancarlospaco commented 3 days ago

Even on an identifier name left-hand side? 🤔 In which scenarios having f( "value" = "value" ) is a reasonable expansion? I think should be possible to make the compiler at least produce a warning...

beef331 commented 3 days ago

Implementing a template that invokes a macro where you actually want "a" = "b".