-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
stop gensym identifiers hijacking routine decl names in templates (#2…
…3392) fixes #23326 In a routine declaration node in a template, if the routine is marked as `gensym`, the compiler adds it as a new symbol to a preliminary scope of the template. If it's not marked as gensym, then it searches the preliminary scope of the template for the name of the routine, then when it matches a template parameter or a gensym identifier, the compiler replaces the name node with a symbol node of the found symbol. This makes sense for the template parameter since it has to be replaced later, but not really for the gensym identifier, as it doesn't allow us to inject a routine with the same name as an identifier previously declared as gensym (the problem in #23326 is when this is in another `when` branch). However this is the only channel to reuse a gensym symbol in a declaration, so maybe removing it has side effects. For example if we have: ```nim proc foo(x: int) {.gensym.} = discard proc foo(x: float) {.gensym.} = discard ``` it will not behave the same as ```nim proc foo(x: int) {.gensym.} = discard proc foo(x: float) = discard ``` behaved previously, which maybe allowed overloading over the gensym'd symbols. A note to the "undeclared identifier" error message has also been added for a potential error code that implicitly depended on the old behavior might give, namely ``undeclared identifier: 'abc`gensym123'``, which happens when in a template an identifier is first declared gensym in code that doesn't compile, then as a routine which injects by default, then the identifier is used. (cherry picked from commit 73b0b0d)
- Loading branch information
Showing
5 changed files
with
71 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
discard """ | ||
cmd: "nim check --hints:off $file" | ||
""" | ||
|
||
block: | ||
template foo = | ||
when false: | ||
let x = 123 | ||
else: | ||
template x: untyped = 456 | ||
echo x #[tt.Error | ||
^ undeclared identifier: 'x`gensym0'; if declared in a template, this identifier may be inconsistently marked inject or gensym]# | ||
foo() | ||
|
||
block: | ||
template foo(y: static bool) = | ||
block: | ||
when y: | ||
let x {.gensym.} = 123 | ||
else: | ||
let x {.inject.} = 456 | ||
echo x #[tt.Error | ||
^ undeclared identifier: 'x']# | ||
foo(false) | ||
foo(true) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# issue #23326 | ||
|
||
type Result*[E] = object | ||
e*: E | ||
|
||
proc error*[E](v: Result[E]): E = discard | ||
|
||
template valueOr*[E](self: Result[E], def: untyped): int = | ||
when E isnot void: | ||
when false: | ||
# Comment line below to make it work | ||
template error(): E {.used, gensym.} = s.e | ||
discard | ||
else: | ||
template error(): E {.used, inject.} = | ||
self.e | ||
|
||
def | ||
else: | ||
def | ||
|
||
|
||
block: | ||
let rErr = Result[string](e: "a") | ||
let rErrV = rErr.valueOr: | ||
ord(error[0]) | ||
|
||
block: | ||
template foo(x: static bool): untyped = | ||
when x: | ||
let a = 123 | ||
else: | ||
template a: untyped {.gensym.} = 456 | ||
a | ||
|
||
doAssert foo(false) == 456 | ||
doAssert foo(true) == 123 |