diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 1ffe5aed4a1b7..90376214db681 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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 diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0393d8ec65398..e486f3a47f2c0 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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`` @@ -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 diff --git a/tests/converter/tgenericconverterbindings1.nim b/tests/converter/tgenericconverterbindings1.nim new file mode 100644 index 0000000000000..04d397bde0f29 --- /dev/null +++ b/tests/converter/tgenericconverterbindings1.nim @@ -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 diff --git a/tests/converter/tgenericconverterbindings2.nim b/tests/converter/tgenericconverterbindings2.nim new file mode 100644 index 0000000000000..b2d9ba3d144e2 --- /dev/null +++ b/tests/converter/tgenericconverterbindings2.nim @@ -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 diff --git a/tests/converter/tgenericconverterbindings3.nim b/tests/converter/tgenericconverterbindings3.nim new file mode 100644 index 0000000000000..df37c9dd0890e --- /dev/null +++ b/tests/converter/tgenericconverterbindings3.nim @@ -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]) diff --git a/tests/converter/tgenericconverterbindings4.nim b/tests/converter/tgenericconverterbindings4.nim new file mode 100644 index 0000000000000..abf210b062503 --- /dev/null +++ b/tests/converter/tgenericconverterbindings4.nim @@ -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 diff --git a/tests/converter/tgenericconverterbindings5.nim b/tests/converter/tgenericconverterbindings5.nim new file mode 100644 index 0000000000000..4298a177876f0 --- /dev/null +++ b/tests/converter/tgenericconverterbindings5.nim @@ -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) diff --git a/tests/converter/tgenericconverterbindings6.nim b/tests/converter/tgenericconverterbindings6.nim new file mode 100644 index 0000000000000..4ae0069aa1b2c --- /dev/null +++ b/tests/converter/tgenericconverterbindings6.nim @@ -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, +)