Generic overload ambiguous call for int literal; Regression in 2.0/2.2/devel
Nim Version
Nim Compiler Version 2.3.1 [Linux: amd64] Compiled at 2025-09-13 Copyright (c) 2006-2025 by Andreas Rumpf
git hash: ff9cae896ce900ac8e77ec9a51e09192653a1a49 active boot switches: -d:release
Description
proc foo[T: int](x: T) =
echo "int"
proc foo[T: uint](x: T) =
echo "uint"
foo(0'u) # ok
foo(0'u8) # ok
foo(0'i8) # ok
foo(0.int) # ok
foo(0) # fails
Current Output
ambiguous call error
Expected Output
uint
uint
int
int
int
Known Workarounds
No response
Additional Information
Works in Nim 1.6.20 and 2.0.8, fails in 2.0.10 to 2.0.16, 2.2.0 to 2.2.4 and devel.
!nim c
proc foo[T: int](x: T) =
echo "int"
proc foo[T: uint](x: T) =
echo "uint"
foo(0) # fails
:penguin: Linux bisect by @ringabout (member)
devel :-1: FAIL
Output
Filesize 0 (0 bytes)
Duration
stable :-1: FAIL
Output
Filesize 0 (0 bytes)
Duration
2.2.2 :-1: FAIL
Output
Filesize 0 (0 bytes)
Duration
2.0.0 :+1: OK
Output
Filesize 91.02 Kb (93,208 bytes)
Duration
1.6.20 :+1: OK
Output
Filesize 96.09 Kb (98,400 bytes)
Duration
1.4.8 :+1: OK
Output
Filesize 92.19 Kb (94,400 bytes)
Duration
1.2.18 :+1: OK
Output
Filesize 91.91 Kb (94,112 bytes)
Duration
1.0.10 :+1: OK
Output
Filesize 87.03 Kb (89,120 bytes)
Duration
??? :arrow_right: :bug:
Diagnostics
The commit that introduced the bug can not be found, but the bug is in the commits:
(Can not find the commit because Nim can not be re-built commit-by-commit to bisect).
Stats
- GCC
13.3.0 - Clang
18.1.3 - NodeJS
19.5 - Created
2025-09-17T13:27:20Z - Comments
1 - Commands
nim c --run -d:nimDebug -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim
:robot: Bug found in 27 mins bisecting 1372 commits at 50 commits per second
Not that it's impossible to make this work, but the spec doesn't have any kind of rule that would make int match stronger then uint generically. I'm not sure this is a bug since 0.int works.
I was expecting the constrained generic to match the same as the concrete type proc version, as it seems to be the case for every other type. It also seems to work for generic [T: int] and [T: float] as I'd expect.
proc foo(x: int) =
echo "int"
proc foo(x: uint) =
echo "uint"
foo(0)
# output: int
proc bar[T: int](x: T) =
echo "int"
proc bar[T: float](x: T) =
echo "float"
bar(0)
# output: int
proc baz[T: float](x: T) =
echo "float"
baz(0)
# output: float
Also, the issue also exists for SomeUnsignedInt and SomeSignedInt:
proc foo[T: SomeSignedInt](x: T): string =
"SomeSignedInt"
proc foo[T: SomeUnsignedInt](x: T): string =
"SomeUnsignedInt"
doAssert foo(0'u) == "SomeUnsignedInt"
doAssert foo(0'u8) == "SomeUnsignedInt"
doAssert foo(0'i8) == "SomeSignedInt"
doAssert foo(0.int) == "SomeSignedInt"
doAssert foo(0) == "SomeSignedInt" # ambiguous call error
@nitely
The substitution would be equivalent if both were a generic match. When you make int the generic constraint you are still matching generically with T. I guess var x = 0 is implicitly int but if you use an int literal as an argument to a uint parameter I don't think that is an implicit type conversion from int -> uint. So if an integer literal's type is dependent on the formal parameter's type then it should have no preference to be either signed or unsigned. Of course, I do understand what you are saying though. I could be wrong.