Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,12 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
let s = a[0].sym
if s.isGenericRoutineStrict:
let finalCallee = generateInstance(c, s, x.bindings, a.info)
var src = s.typ.firstParamType
var convMatch = newCandidate(c, src)
let srca = typeRel(convMatch, src, a[1].typ)
if srca notin {isEqual, isGeneric, isSubtype}:
internalError(c.config, a.info, "generic converter failed rematch")
let finalCallee = generateInstance(c, s, convMatch.bindings, a.info)
a[0].sym = finalCallee
a[0].typ() = finalCallee.typ
#a.typ = finalCallee.typ.returnType
Expand Down
5 changes: 3 additions & 2 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
# for generic type converters we need to check 'src <- a' before
# 'f <- dest' in order to not break the unification:
# see tests/tgenericconverter:
let srca = typeRel(m, src, a)
var convMatch = newCandidate(c, src)
let srca = typeRel(convMatch, src, a)
if srca notin {isEqual, isGeneric, isSubtype}: continue

# What's done below matches the logic in ``matchesAux``
Expand All @@ -2308,7 +2309,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,

let destIsGeneric = containsGenericType(dest)
if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest)
dest = generateTypeInstance(c, convMatch.bindings, arg, dest)
let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
# can't fully mark used yet, may not be used in final call
Expand Down
37 changes: 37 additions & 0 deletions tests/converter/tgenericconverterbindings1.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
discard """
output: '''
p 1 20
p 1000 200
p 1 1
p 1000 1000
p 1 1000
p 1000 1
p 1 200
p 1000 20
'''
"""

# issue #4554

type
G[N:static[int]] = object
v: int
F[N:static[int]] = object
v: int

converter G2int[N:static[int]](x:G[N]):int = x.v
converter F2int[N:static[int]](x:F[N]):int = x.v
proc p(x,y:int) = echo "p ",x," ",y
var
g1 = G[1](v:1)
g2 = G[2](v:20)
f1 = F[1](v:1000)
f2 = F[2](v:200)
p(g1,g2) # Error: type mismatch: got (G[1], G[2])
p(f1,f2) # Error: type mismatch: got (F[1], F[2])
p(g1,g1) # compiles
p(f1,f1) # compiles
p(g1,f1) # compiles
p(f1,g1) # compiles
p(g1,f2) # compiles
p(f1,g2) # compiles
10 changes: 10 additions & 0 deletions tests/converter/tgenericconverterbindings2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# issue #4554 comment

type Obj[T] = object
b: T

converter test1[T](a: Obj[T]): T = a.b

proc doStuff(a: int, b: float) = discard

doStuff(Obj[int](b: 1), Obj[float](b: 1.2)) # Error: type mismatch: got <Obj[system.int], Obj[system.float]>
38 changes: 38 additions & 0 deletions tests/converter/tgenericconverterbindings3.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# issue #10900

import std/options

type
AllTypesInModule =
bool | string | seq[int]

converter toOptional[T: AllTypesInModule](x: T): Option[T] =
some(x)

proc foo(
a: Option[bool] = none[bool](),
b: Option[string] = none[string](),
c: Option[seq[int]] = none[seq[int]]()) =
discard

# works:
foo(a = true)
foo(true)
foo(b = "asdf")
foo(c = @[1, 2, 3])

# fails:
foo(
a = true,
b = "asdf")
foo(true, "asdf")
foo(
a = true,
c = @[1, 2, 3])
foo(
b = "asdf",
c = @[1, 2, 3])
foo(
a = true,
b = "asdf",
c = @[1, 2, 3])
19 changes: 19 additions & 0 deletions tests/converter/tgenericconverterbindings4.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# issue #13843

type
IdLayer {.pure, size: int.sizeof.} = enum
Core
Ui
IdScene {.pure, size: int.sizeof.} = enum
Game
Shop
SomeIds = IdLayer|IdScene

converter toint*(x: SomeIds): int = x.int

var IdGame : int = IdScene.Game #works

proc bind_scene(a, b: int) = discard
bind_scene(Core,Game) # doesn't work, type mismatch for Game, doesnt convert to int
bind_scene(Core,IdScene.Game) # doesn't work, type mismatch for Game, doesnt convert to int
bind_scene(Core,IdGame) # works
41 changes: 41 additions & 0 deletions tests/converter/tgenericconverterbindings5.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
discard """
output: '''
Converting (int, int) to A
Converting (int, int) to A
Checked: A
Checked: A
Checked: A
Converting (A, A) to A
Converting (int, int) to A
Checked: A
Checked: A
Checked: A
Converting (A, A) to A
Converting (A, A) to A
Checked: A
Checked: A
Checked: A
'''
"""

# issue #19471

type A = ref object

converter toA(x: tuple): A =
echo "Converting ", x.type, " to A"
A()

proc check(a: A) =
echo "Checked: ", a.type

proc mux(a: A, b: A, c: A) =
check(a)
check(b)
check(c)

let a = A()

mux(a, (0, 0), (1, 1)) # both tuples are (int, int)
mux(a, (a, a), (1, 1)) # one tuple is (A, A), another (int, int)
mux(a, (a, a), (a, a)) # both tuples are (A, A)
36 changes: 36 additions & 0 deletions tests/converter/tgenericconverterbindings6.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
discard """
output: '''
int | int
int | string
int | string
'''
"""

# issue #19517

type thing [T] = object
value: T

converter asValue[T](o: thing[T]): T =
o.value

proc mycall(num, num2: int) =
echo ($(num.type) & " | " & $(num2.type))

proc mycall(num: int, str: string) =
echo ($(num.type) & " | " & $(str.type))

mycall( # This call uses asValue[int] converter automatically fine
thing[int](value: 1),
thing[int](value: 42),
)

mycall( # This gives a type error as if the converter was not defined and I tried to pass in a thing directly
thing[int](value: 2),
thing[string](value: "foo"),
)

mycall( # This can be fixed by calling the converter explicitly for everything but the first use
thing[int](value: 2),
thing[string](value: "foo").asValue,
)