Skip to content

Commit 334f96c

Browse files
authored
isolate and rematch generic converters to get bindings (#24867)
fixes #4554, fixes #10900, fixes #13843, fixes #19471, fixes #19517 Instead of matching generic converters to their arguments using the full call match bindings, a new match is created for them (from which the bindings are used to instantiate the converter return type). Then when instantiating generic converters, they are matched to their argument again to get their bindings again instead of using the call bindings. This prevents generic converters which match more than once from interfering with each other's bindings.
1 parent 0cba752 commit 334f96c

File tree

8 files changed

+190
-3
lines changed

8 files changed

+190
-3
lines changed

compiler/semcall.nim

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,12 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
686686
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
687687
let s = a[0].sym
688688
if s.isGenericRoutineStrict:
689-
let finalCallee = generateInstance(c, s, x.bindings, a.info)
689+
var src = s.typ.firstParamType
690+
var convMatch = newCandidate(c, src)
691+
let srca = typeRel(convMatch, src, a[1].typ)
692+
if srca notin {isEqual, isGeneric, isSubtype}:
693+
internalError(c.config, a.info, "generic converter failed rematch")
694+
let finalCallee = generateInstance(c, s, convMatch.bindings, a.info)
690695
a[0].sym = finalCallee
691696
a[0].typ() = finalCallee.typ
692697
#a.typ = finalCallee.typ.returnType

compiler/sigmatch.nim

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,7 +2296,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
22962296
# for generic type converters we need to check 'src <- a' before
22972297
# 'f <- dest' in order to not break the unification:
22982298
# see tests/tgenericconverter:
2299-
let srca = typeRel(m, src, a)
2299+
var convMatch = newCandidate(c, src)
2300+
let srca = typeRel(convMatch, src, a)
23002301
if srca notin {isEqual, isGeneric, isSubtype}: continue
23012302

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

23092310
let destIsGeneric = containsGenericType(dest)
23102311
if destIsGeneric:
2311-
dest = generateTypeInstance(c, m.bindings, arg, dest)
2312+
dest = generateTypeInstance(c, convMatch.bindings, arg, dest)
23122313
let fdest = typeRel(m, f, dest)
23132314
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
23142315
# can't fully mark used yet, may not be used in final call
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
discard """
2+
output: '''
3+
p 1 20
4+
p 1000 200
5+
p 1 1
6+
p 1000 1000
7+
p 1 1000
8+
p 1000 1
9+
p 1 200
10+
p 1000 20
11+
'''
12+
"""
13+
14+
# issue #4554
15+
16+
type
17+
G[N:static[int]] = object
18+
v: int
19+
F[N:static[int]] = object
20+
v: int
21+
22+
converter G2int[N:static[int]](x:G[N]):int = x.v
23+
converter F2int[N:static[int]](x:F[N]):int = x.v
24+
proc p(x,y:int) = echo "p ",x," ",y
25+
var
26+
g1 = G[1](v:1)
27+
g2 = G[2](v:20)
28+
f1 = F[1](v:1000)
29+
f2 = F[2](v:200)
30+
p(g1,g2) # Error: type mismatch: got (G[1], G[2])
31+
p(f1,f2) # Error: type mismatch: got (F[1], F[2])
32+
p(g1,g1) # compiles
33+
p(f1,f1) # compiles
34+
p(g1,f1) # compiles
35+
p(f1,g1) # compiles
36+
p(g1,f2) # compiles
37+
p(f1,g2) # compiles
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# issue #4554 comment
2+
3+
type Obj[T] = object
4+
b: T
5+
6+
converter test1[T](a: Obj[T]): T = a.b
7+
8+
proc doStuff(a: int, b: float) = discard
9+
10+
doStuff(Obj[int](b: 1), Obj[float](b: 1.2)) # Error: type mismatch: got <Obj[system.int], Obj[system.float]>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# issue #10900
2+
3+
import std/options
4+
5+
type
6+
AllTypesInModule =
7+
bool | string | seq[int]
8+
9+
converter toOptional[T: AllTypesInModule](x: T): Option[T] =
10+
some(x)
11+
12+
proc foo(
13+
a: Option[bool] = none[bool](),
14+
b: Option[string] = none[string](),
15+
c: Option[seq[int]] = none[seq[int]]()) =
16+
discard
17+
18+
# works:
19+
foo(a = true)
20+
foo(true)
21+
foo(b = "asdf")
22+
foo(c = @[1, 2, 3])
23+
24+
# fails:
25+
foo(
26+
a = true,
27+
b = "asdf")
28+
foo(true, "asdf")
29+
foo(
30+
a = true,
31+
c = @[1, 2, 3])
32+
foo(
33+
b = "asdf",
34+
c = @[1, 2, 3])
35+
foo(
36+
a = true,
37+
b = "asdf",
38+
c = @[1, 2, 3])
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# issue #13843
2+
3+
type
4+
IdLayer {.pure, size: int.sizeof.} = enum
5+
Core
6+
Ui
7+
IdScene {.pure, size: int.sizeof.} = enum
8+
Game
9+
Shop
10+
SomeIds = IdLayer|IdScene
11+
12+
converter toint*(x: SomeIds): int = x.int
13+
14+
var IdGame : int = IdScene.Game #works
15+
16+
proc bind_scene(a, b: int) = discard
17+
bind_scene(Core,Game) # doesn't work, type mismatch for Game, doesnt convert to int
18+
bind_scene(Core,IdScene.Game) # doesn't work, type mismatch for Game, doesnt convert to int
19+
bind_scene(Core,IdGame) # works
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
discard """
2+
output: '''
3+
Converting (int, int) to A
4+
Converting (int, int) to A
5+
Checked: A
6+
Checked: A
7+
Checked: A
8+
Converting (A, A) to A
9+
Converting (int, int) to A
10+
Checked: A
11+
Checked: A
12+
Checked: A
13+
Converting (A, A) to A
14+
Converting (A, A) to A
15+
Checked: A
16+
Checked: A
17+
Checked: A
18+
'''
19+
"""
20+
21+
# issue #19471
22+
23+
type A = ref object
24+
25+
converter toA(x: tuple): A =
26+
echo "Converting ", x.type, " to A"
27+
A()
28+
29+
proc check(a: A) =
30+
echo "Checked: ", a.type
31+
32+
proc mux(a: A, b: A, c: A) =
33+
check(a)
34+
check(b)
35+
check(c)
36+
37+
let a = A()
38+
39+
mux(a, (0, 0), (1, 1)) # both tuples are (int, int)
40+
mux(a, (a, a), (1, 1)) # one tuple is (A, A), another (int, int)
41+
mux(a, (a, a), (a, a)) # both tuples are (A, A)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
discard """
2+
output: '''
3+
int | int
4+
int | string
5+
int | string
6+
'''
7+
"""
8+
9+
# issue #19517
10+
11+
type thing [T] = object
12+
value: T
13+
14+
converter asValue[T](o: thing[T]): T =
15+
o.value
16+
17+
proc mycall(num, num2: int) =
18+
echo ($(num.type) & " | " & $(num2.type))
19+
20+
proc mycall(num: int, str: string) =
21+
echo ($(num.type) & " | " & $(str.type))
22+
23+
mycall( # This call uses asValue[int] converter automatically fine
24+
thing[int](value: 1),
25+
thing[int](value: 42),
26+
)
27+
28+
mycall( # This gives a type error as if the converter was not defined and I tried to pass in a thing directly
29+
thing[int](value: 2),
30+
thing[string](value: "foo"),
31+
)
32+
33+
mycall( # This can be fixed by calling the converter explicitly for everything but the first use
34+
thing[int](value: 2),
35+
thing[string](value: "foo").asValue,
36+
)

0 commit comments

Comments
 (0)