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.23k stars 1.46k forks source link

Regression from 2.0 to `devel` with `dirty` template #23611

Open tersec opened 1 month ago

tersec commented 1 month ago

Description

a.nim

import "."/b

type
  B = object
  L = object

template F(T: type B): type = F(Json, B)
var j = F(B).init()
var f: L
s(j, f)

b.nim

type P = object

template d(Name: untyped) {.dirty.} =
  type Name* = object

import "."/c

d(Json)

type K[Flavor = P] = object
  lex: V

template F*(T: type Json, F: distinct type = P): type = K[F]

proc init*(T: type K): T = discard

proc s*[T](r: var K, value: var T) =
  x(r.lex)

c.nim

type
  U* = enum
    errNone
  V* = object
    err: U

template x*(lex: V) {.dirty.} =
  lex.err = errNone

Nim Version

Compiles:

Nim Compiler Version 2.0.5 [Linux: amd64]
Compiled at 2024-05-15
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: 2f399807cb45ea073e1c1bb640d16956e58fcae0
active boot switches: -d:release

Does not compile:

Nim Compiler Version 2.1.1 [Linux: amd64]
Compiled at 2024-05-15
Copyright (c) 2006-2024 by Andreas Rumpf

git hash: b42f1ca8a4d574b087af9d86b7627a5817dad9b0
active boot switches: -d:release

Current Output

a.nim(10, 2) template/generic instantiation of `s` from here
b.nim(18, 4) template/generic instantiation of `x` from here
c.nim(8, 13) Error: undeclared identifier: 'errNone'
candidates (edit distance, scope distance); see '--spellSuggest': 
 (4, 5): 'NimNode'
 (4, 5): 'range'

Expected Output

No response

Possible Solution

No response

Additional Information

No response

metagn commented 1 month ago

Simplified:

# a.nim
import "."/b

s[int]()
# b.nim
import "."/c

type K = object
  lex: V

proc s*[T]() =
  var r: K
  x(r.lex)
# c.nim
type
  U* = enum
    errNone
  V* = object
    err: U

template x*(lex: V) {.dirty.} =
  lex.err = errNone

Removing the lex: V parameter from x gives no error, but changing lex.err = to let y = or errNone to a const instead of an enum symbol gives the same error on 2.0. dirty doesn't bind anything by default, so errNone was never accessible as a symbol through lookup.

So this very likely fails now because of #23588, which again, means this only worked since errNone is specifically an enum member in a context where its type can be inferred. I don't know if it's worth making this specific case work by adding back awkward behavior to the compiler. Is it too much of a problem to either bind errNone or not use dirty here?

jangko commented 1 month ago

We need the dirty template because sometimes we pass in statement such as return. But using bind also works.

tersec commented 1 month ago

To the extent https://github.com/status-im/nim-json-serialization/pull/90 solves this, I'm fine with that. I reported this as a regression fairly mechanically, not so much prescriptively as descriptively. But I tend to agree it accidentally hit a weird edge case.