Skip to content

Commit 1cc4d3f

Browse files
authored
fix generic param substitution in templates (#22535)
* fix generic param substitution in templates fixes #13527, fixes #17240, fixes #6340, fixes #20033, fixes #19576, fixes #19076 * fix bare except in test, test updated packages in CI
1 parent d677ed3 commit 1cc4d3f

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

compiler/sem.nim

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,13 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
473473
# we now know the supplied arguments
474474
var paramTypes = initIdTable()
475475
for param, value in genericParamsInMacroCall(s, call):
476-
idTablePut(paramTypes, param.typ, value.typ)
476+
var givenType = value.typ
477+
# the sym nodes used for the supplied generic arguments for
478+
# templates and macros leave type nil so regular sem can handle it
479+
# in this case, get the type directly from the sym
480+
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
481+
givenType = value.sym.typ
482+
idTablePut(paramTypes, param.typ, givenType)
477483

478484
retType = generateTypeInstance(c, paramTypes,
479485
macroResult.info, retType)

compiler/semcall.nim

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
655655
else:
656656
x.call.add c.graph.emptyNode
657657
of skType:
658-
x.call.add newSymNode(s, n.info)
658+
var tn = newSymNode(s, n.info)
659+
# this node will be used in template substitution,
660+
# pretend this is an untyped node and let regular sem handle the type
661+
# to prevent problems where a generic parameter is treated as a value
662+
tn.typ = nil
663+
x.call.add tn
659664
else:
660665
internalAssert c.config, false
661666

tests/template/tgenericparam.nim

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
block: # basic template generic parameter substitution
2+
block: # issue #13527
3+
template typeNameTempl[T](a: T): string = $T
4+
proc typeNameProc[T](a: T): string = $T
5+
doAssert typeNameTempl(1) == typeNameProc(1)
6+
doAssert typeNameTempl(true) == typeNameProc(true)
7+
doAssert typeNameTempl(1.0) == typeNameProc(1.0)
8+
doAssert typeNameTempl(1u8) == typeNameProc(1u8)
9+
10+
template isDefault[T](a: T): bool = a == default(T)
11+
doAssert isDefault(0.0)
12+
13+
block: # issue #17240
14+
func to(c: int, t: typedesc[float]): t = discard
15+
template converted[I, T](i: seq[I], t: typedesc[T]): seq[T] =
16+
var result = newSeq[T](2)
17+
result[0] = i[0].to(T)
18+
result
19+
doAssert newSeq[int](3).converted(float) == @[0.0, 0.0]
20+
21+
block: # issue #6340
22+
type A[T] = object
23+
v: T
24+
proc foo(x: int): string = "int"
25+
proc foo(x: typedesc[int]): string = "typedesc[int]"
26+
template fooT(x: int): string = "int"
27+
template fooT(x: typedesc[int]): string = "typedesc[int]"
28+
proc foo[T](x: A[T]): (string, string) =
29+
(foo(T), fooT(T))
30+
template fooT[T](x: A[T]): (string, string) =
31+
(foo(T), fooT(T))
32+
var x: A[int]
33+
doAssert foo(x) == fooT(x)
34+
35+
block: # issue #20033
36+
template run[T](): T = default(T)
37+
doAssert run[int]() == 0
38+
39+
import options, tables
40+
41+
block: # complex cases of above with imports
42+
block: # issue #19576, complex case
43+
type RegistryKey = object
44+
key, val: string
45+
var regKey = @[RegistryKey(key: "abc", val: "def")]
46+
template findFirst[T](s: seq[T], pred: proc(x: T): bool): Option[T] =
47+
var res = none(T) # important line
48+
for x in s:
49+
if pred(x):
50+
res = some(x)
51+
break
52+
res
53+
proc getval(searchKey: string): Option[string] =
54+
let found = regKey.findFirst(proc (rk: RegistryKey): bool = rk.key == searchKey)
55+
if found.isNone: none(string)
56+
else: some(found.get().val)
57+
doAssert getval("strange") == none(string)
58+
doAssert getval("abc") == some("def")
59+
block: # issue #19076
60+
block: # case 1
61+
var tested: Table[string,int]
62+
template `[]`[V](t:Table[string,V],key:string):untyped =
63+
$V
64+
doAssert tested["abc"] == "int"
65+
template `{}`[V](t:Table[string,V],key:string):untyped =
66+
($V, tables.`[]`(t, key))
67+
doAssert (try: tested{"abc"} except KeyError: ("not there", 123)) == ("not there", 123)
68+
tables.`[]=`(tested, "abc", 456)
69+
doAssert tested["abc"] == "int"
70+
doAssert tested{"abc"} == ("int", 456)
71+
block: # case 2
72+
type Foo[A,T] = object
73+
t:T
74+
proc init[A,T](f:type Foo,a:typedesc[A],t:T):Foo[A,T] = Foo[A,T](t:t)
75+
template fromOption[A](o:Option[A]):auto =
76+
when o.isSome:
77+
Foo.init(A,35)
78+
else:
79+
Foo.init(A,"hi")
80+
let op = fromOption(some(5))

0 commit comments

Comments
 (0)