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

Nested closure is tagged `{.nimcall.}` when `{.closure.}` is expected #23919

Open itspomf opened 1 month ago

itspomf commented 1 month ago

Description

When calling a proc which expects a closure, it is not possible to nest a secondary closure within it, as illustrated in case 2 below.

When doing so, the compiler raises an error regarding a calling convention mismatch, warning that it got the {.nimcall.} pragma as opposed to {.closure.}.

# Return a duplicate of Table t where all values satisfy condition c.
proc keepIf2[A, B]( t: var Table[A, B], c: proc( v: B ): bool {.closure.} ): Table[A, B] =
  var dup = initTable[A, B]( t.len )
  for k, v in t.pairs:
    if c( v ): dup[k] = v
  return dup

# Test code:
# case 1 (passing)
var table = { 'A': 1, 'B': 2, 'C': 3, 'D': 4 }.toTable
var r = table.keepIf2( proc(value: int): bool =
  value mod 2 == 0 )
assert {'B': 2, 'D': 4}.toTable == r

# case 2 (fails)
# 'do' expression used for legibility
var nested = { 'A': { 'a': 1 }, 'B': { 'b': 2 }, 'C': { 'c': 3 }}.toTable
var r2 = nested.keepIf2 do (sub: var Table[ char, Table[ char, int]]) -> bool:
  sub.keepIf2 do (v: int) -> bool: # <-- compile-time error here
    v mod 2 != 0
assert {'A': { 'a': 1 }, 'C': {'c': 3 }}.toTable == r2

Nim Version

Nim 2.0.8

Current Output

/usercode/in.nim(46, 6) Error: type mismatch
Expression: keepIf2(sub, proc (v: int): bool = result =
  not (v mod 2 == 0))
  [1] sub: Table[system.char, Table[system.char, system.int]]
  [2] proc (v: int): bool = result =
  not (v mod 2 == 0): proc (v: int): bool{.noSideEffect, gcsafe.}

Expected one of (first mismatch at [position]):
[2] proc keepIf2[A, B](t: var Table[A, B]; c: proc (v: B): bool {.closure.}): Table[
    A, B]
  expression 'proc (v: int): bool = result =
  not (v mod 2 == 0)' is of type: proc (v: int): bool{.noSideEffect, gcsafe.}
  Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.

Expected Output

Program should compile and exit without errors.

Possible Solution

No response

Additional Information

No response

metagn commented 1 month ago

It should be tagged nimcall, it doesn't capture anything and nimcall is convertible to closure. It errors even when you tag the nested closure as closure. It doesn't have to be a nested closure either:

import tables

proc keepIf2[A, B]( t: var Table[A, B], c: proc( v: B ): bool {.closure.} ): Table[A, B] =
  discard

proc foo(sub: var Table[ char, Table[ char, int]]) =
  discard sub.keepIf2 do (v: int) -> bool {.closure.}: # <-- compile-time error here
    v mod 2 != 0

Are you sure this should compile? It seems like the signature of the closure should be (v: Table[char, int]) -> bool instead. If so, maybe the error message could be improved to show the bound generic parameters?