Skip to content

Commit

Permalink
fix generic param substitution in templates (#22535)
Browse files Browse the repository at this point in the history
* 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

(cherry picked from commit 1cc4d3f)
  • Loading branch information
metagn authored and narimiran committed Dec 1, 2023
1 parent add124e commit 3ab6012
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
8 changes: 7 additions & 1 deletion compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,13 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# we now know the supplied arguments
var paramTypes = newIdTable()
for param, value in genericParamsInMacroCall(s, call):
idTablePut(paramTypes, param.typ, value.typ)
var givenType = value.typ
# the sym nodes used for the supplied generic arguments for
# templates and macros leave type nil so regular sem can handle it
# in this case, get the type directly from the sym
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
givenType = value.sym.typ
idTablePut(paramTypes, param.typ, givenType)

retType = generateTypeInstance(c, paramTypes,
macroResult.info, retType)
Expand Down
7 changes: 6 additions & 1 deletion compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,12 @@ proc semResolvedCall(c: PContext, x: TCandidate,
else:
x.call.add c.graph.emptyNode
of skType:
x.call.add newSymNode(s, n.info)
var tn = newSymNode(s, n.info)
# this node will be used in template substitution,
# pretend this is an untyped node and let regular sem handle the type
# to prevent problems where a generic parameter is treated as a value
tn.typ = nil
x.call.add tn
else:
internalAssert c.config, false

Expand Down
80 changes: 80 additions & 0 deletions tests/template/tgenericparam.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
block: # basic template generic parameter substitution
block: # issue #13527
template typeNameTempl[T](a: T): string = $T
proc typeNameProc[T](a: T): string = $T
doAssert typeNameTempl(1) == typeNameProc(1)
doAssert typeNameTempl(true) == typeNameProc(true)
doAssert typeNameTempl(1.0) == typeNameProc(1.0)
doAssert typeNameTempl(1u8) == typeNameProc(1u8)

template isDefault[T](a: T): bool = a == default(T)
doAssert isDefault(0.0)

block: # issue #17240
func to(c: int, t: typedesc[float]): t = discard
template converted[I, T](i: seq[I], t: typedesc[T]): seq[T] =
var result = newSeq[T](2)
result[0] = i[0].to(T)
result
doAssert newSeq[int](3).converted(float) == @[0.0, 0.0]

block: # issue #6340
type A[T] = object
v: T
proc foo(x: int): string = "int"
proc foo(x: typedesc[int]): string = "typedesc[int]"
template fooT(x: int): string = "int"
template fooT(x: typedesc[int]): string = "typedesc[int]"
proc foo[T](x: A[T]): (string, string) =
(foo(T), fooT(T))
template fooT[T](x: A[T]): (string, string) =
(foo(T), fooT(T))
var x: A[int]
doAssert foo(x) == fooT(x)

block: # issue #20033
template run[T](): T = default(T)
doAssert run[int]() == 0

import options, tables

block: # complex cases of above with imports
block: # issue #19576, complex case
type RegistryKey = object
key, val: string
var regKey = @[RegistryKey(key: "abc", val: "def")]
template findFirst[T](s: seq[T], pred: proc(x: T): bool): Option[T] =
var res = none(T) # important line
for x in s:
if pred(x):
res = some(x)
break
res
proc getval(searchKey: string): Option[string] =
let found = regKey.findFirst(proc (rk: RegistryKey): bool = rk.key == searchKey)
if found.isNone: none(string)
else: some(found.get().val)
doAssert getval("strange") == none(string)
doAssert getval("abc") == some("def")
block: # issue #19076
block: # case 1
var tested: Table[string,int]
template `[]`[V](t:Table[string,V],key:string):untyped =
$V
doAssert tested["abc"] == "int"
template `{}`[V](t:Table[string,V],key:string):untyped =
($V, tables.`[]`(t, key))
doAssert (try: tested{"abc"} except KeyError: ("not there", 123)) == ("not there", 123)
tables.`[]=`(tested, "abc", 456)
doAssert tested["abc"] == "int"
doAssert tested{"abc"} == ("int", 456)
block: # case 2
type Foo[A,T] = object
t:T
proc init[A,T](f:type Foo,a:typedesc[A],t:T):Foo[A,T] = Foo[A,T](t:t)
template fromOption[A](o:Option[A]):auto =
when o.isSome:
Foo.init(A,35)
else:
Foo.init(A,"hi")
let op = fromOption(some(5))

0 comments on commit 3ab6012

Please sign in to comment.