mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 13:30:33 +00:00
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
(cherry picked from commit 1cc4d3f622)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
80
tests/template/tgenericparam.nim
Normal file
80
tests/template/tgenericparam.nim
Normal file
@@ -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))
|
||||
Reference in New Issue
Block a user