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

Infinite recursion instantiating generic defined on a concept #24463

Open c-blake opened 1 day ago

c-blake commented 1 day ago

Description

It's possible this could be shrunken a little, but it's pretty minimal.

type
  Oat*[K, Q] = concept t
    eq(t, K, K) is bool
    key(t, int) is K

  Valued*[V] = concept t
    val(var t, int, V)
    val(t, int) is V

  VOat*[K,Q,V] = concept t
    t is Valued[V]
    t is Oat[K,Q]

proc getOrDefault*[K,Q,V](t: VOat[K,Q,V], q: Q, def=default(V)): V = discard

type
  Count = object
    cnt: int
    key: string
  Counts = object
    dat: seq[Count]

proc eq(t: Counts, a: string, b: string): bool = a == b
proc key(c: Counts, i: int): string = c.dat[i].key
proc val(c: var Counts, i: int, v: int) = c.dat[i].cnt = v
proc val(c: Counts, i: int): int = c.dat[i].cnt

proc foo() =
  var h: Counts
  when defined bug: # infinite loops compiler & nimsuggest
    let x = getOrDefault(h, "hi")
  else:
    let x = getOrDefault[string, string, int](h, "hi") # works

Nim Version

Compiling with nim c -d:bug makes all versions of Nim I have around infinite loop. This is at least 0.20.2, 1.2, 1.4, 1.6, 2.0, and current devel head.

Current Output

None.  Infinite loop with memory exhaustion.

Expected Output

Either correct compilation with the instantiated inferred or an instantiation error.

Known Workarounds

Always provide the generic parameter list with instance values. It is pretty bothersome need nimsuggest to infinite recurse as a "signal" that one needs to do this, however.

Additional Information

EDIT: The def=default(V) might look troubling to some, but dropping that defaulted parameter entirely seems to not change anything, but I'd like for that to work as well and so kept it in the code example in case this becomes a test case of some kind.