Problems with generic types that mix ddata and function prototypes
Nim Version
Nim version:
Nim Compiler Version 2.2.2 [Linux: amd64]
Compiled at 2025-02-06
Copyright (c) 2006-2025 by Andreas Rumpf
git hash: 6c34f62785263ad412f662f3e4e4bf8d8751d113
active boot switches: -d:release
Description
Test code:
#!/bin/env -S nim -f --assertions:on r
#
import std/strutils
type
parm_func = proc (x: char): bool {. nosideeffect .}
parm_compare = parm_func or set[char] or char or SomeInteger
template test_func(f: parm_compare) : string =
"func: " & $type(f)
template test_func1(f: parm_func) : string =
"func: " & $type(f)
echo "type of set(char) => ", test_func(HexDigits)
echo "type of isDigit => ", test_func1(isDigit)
# The above lines work OK but the following line fails to compile
# i.e. if the following line is commented out it works as expected.
echo "type of isDigit => ", test_func(isDigit)
Current Output
Hint: used config file '/home/ggb/.choosenim/toolchains/nim-2.2.2/config/nim.cfg' [Conf]
Hint: used config file '/home/ggb/.choosenim/toolchains/nim-2.2.2/config/config.nims' [Conf]
Hint: used config file '/home/ggb/Dev/nim.cfg' [Conf]
Hint: used config file '/home/ggb/Dev/nim-pkgs/nim.cfg' [Conf]
.......................................................................................
/home/ggb/Dev/nim-pkgs/string_extras/temp/match_len.nim(22, 38) Error: type mismatch
Expression: test_func(isDigit)
[1] isDigit: proc (c: char): bool{.noSideEffect, gcsafe.}
Expected one of (first mismatch at [position]):
[1] template test_func(f: parm_compare): string
Expected Output
Known Workarounds
No response
Additional Information
No response
The issue is that param_compare is a generic and has a procedure type that is a closure. As it's a closure it requires a conversion and Nim does not do conversion into generic parameters.
Correctly annotating type parm_func = proc (x: char): bool {.nosideeffect, nimcall.} makes it work as intended as it no longer needs to convert to a closure.
Thank you, that has worked.
Is that somewhere in a part of the documentation that I had not read, or just something one should know by intuition?
#4902, the matched types in the or type need to match better than a conversion match, see here. Don't know why, this cap was upgraded to isIntConv from isSubtype just 3 years ago in #20522. Maybe just change it to > isNone? - edit: first commited here: https://github.com/nim-lang/Nim/commit/39e4e3f20570e804e3059574eafe8f78b2d8a9df
and types require matches better or equal to a subtype match which somewhat makes sense
Edit: Actually I do know why, it's because the inferred type ends up being the type of the given argument, not the type to be converted to, the reason for #24250.
Maybe just change it to
> isNone?
At this point, the reason to go > isNone is to maximize signature compatibility at the expense of increasing ambiguity. I don't know if it's worth it. In a lot of scenarios where things are convertible one can A(b). Maybe that is a better approach. Another problem with tyOr is that complexity analysis doesn't work correctly. I think there is practically always going to be some slop with these.
let isDigitClosure: proc (x: char): bool {. nosideeffect, closure .} = isDigit
echo "type of isDigit => ", test_func(isDigitClosure)
From what you are discussing here it looks like this might explain another problem I had with a similar list of or'ed types in a generic.
It had worked last time I looked at it (a long time ago) and suddenly stopped working. I found a way of making it work but because I did not spend the time to disentangle to code to isolate why it had stopped working I had nothing that I could clearly identify that report as an issue, but it looks like this might possibly (not proven, but possible) have been the same matter in a different form.