sigmatch: support optional params with last block arg(s) (#18631)
* sigmatch: support optional params with last block arg * add tests * works with multiple block args * cleanup * address comment
This commit is contained in:
parent
13b9729183
commit
f2910077ac
2 changed files with 94 additions and 1 deletions
|
|
@ -2305,6 +2305,17 @@ proc incrIndexType(t: PType) =
|
||||||
template isVarargsUntyped(x): untyped =
|
template isVarargsUntyped(x): untyped =
|
||||||
x.kind == tyVarargs and x[0].kind == tyUntyped
|
x.kind == tyVarargs and x[0].kind == tyUntyped
|
||||||
|
|
||||||
|
proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
|
||||||
|
# see https://github.com/nim-lang/RFCs/issues/405
|
||||||
|
result = int.high
|
||||||
|
for a2 in countdown(n.len-1, 0):
|
||||||
|
# checking `nfBlockArg in n[a2].flags` wouldn't work inside templates
|
||||||
|
if n[a2].kind != nkStmtList: break
|
||||||
|
let formalLast = m.callee.n[m.callee.n.len - (n.len - a2)]
|
||||||
|
if formalLast.kind == nkSym and formalLast.sym.ast == nil:
|
||||||
|
result = a2
|
||||||
|
else: break
|
||||||
|
|
||||||
proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =
|
proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =
|
||||||
|
|
||||||
template noMatch() =
|
template noMatch() =
|
||||||
|
|
@ -2345,7 +2356,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||||
formalLen = m.callee.n.len
|
formalLen = m.callee.n.len
|
||||||
formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter
|
formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter
|
||||||
container: PNode = nil # constructed container
|
container: PNode = nil # constructed container
|
||||||
|
let firstArgBlock = findFirstArgBlock(m, n)
|
||||||
while a < n.len:
|
while a < n.len:
|
||||||
c.openShadowScope
|
c.openShadowScope
|
||||||
|
|
||||||
|
|
@ -2441,6 +2452,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||||
if m.callee.n[f].kind != nkSym:
|
if m.callee.n[f].kind != nkSym:
|
||||||
internalError(c.config, n[a].info, "matches")
|
internalError(c.config, n[a].info, "matches")
|
||||||
noMatch()
|
noMatch()
|
||||||
|
if a >= firstArgBlock: f = max(f, m.callee.n.len - (n.len - a))
|
||||||
formal = m.callee.n[f].sym
|
formal = m.callee.n[f].sym
|
||||||
m.firstMismatch.kind = kTypeMismatch
|
m.firstMismatch.kind = kTypeMismatch
|
||||||
if containsOrIncl(marker, formal.position) and container.isNil:
|
if containsOrIncl(marker, formal.position) and container.isNil:
|
||||||
|
|
|
||||||
81
tests/misc/trfc405.nim
Normal file
81
tests/misc/trfc405.nim
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
# https://github.com/nim-lang/RFCs/issues/405
|
||||||
|
|
||||||
|
template main =
|
||||||
|
template fn1(a = 1, b = 2, body): auto = (a, b, astToStr(body))
|
||||||
|
let a1 = fn1(10, 20):
|
||||||
|
foo
|
||||||
|
doAssert a1 == (10, 20, "\nfoo")
|
||||||
|
|
||||||
|
template fn2(a = 1, b = 2, body): auto = (a, b, astToStr(body))
|
||||||
|
let a2 = fn2(a = 10): foo
|
||||||
|
doAssert a2 == (10, 2, "\nfoo")
|
||||||
|
let a2b = fn2(b = 20): foo
|
||||||
|
doAssert a2b == (1, 20, "\nfoo")
|
||||||
|
|
||||||
|
template fn3(x: int, a = 1, b = 2, body): auto = (a, b, astToStr(body))
|
||||||
|
let a3 = fn3(3, 10, 20): foo
|
||||||
|
doAssert a3 == (10, 20, "\nfoo")
|
||||||
|
let a3b = fn3(3, a = 10): foo
|
||||||
|
doAssert a3b == (10, 2, "\nfoo")
|
||||||
|
|
||||||
|
template fn4(x: int, y: int, body): auto = (x, y, astToStr(body))
|
||||||
|
let a4 = fn4(1, 2): foo
|
||||||
|
doAssert a4 == (1, 2, "\nfoo")
|
||||||
|
|
||||||
|
template fn5(x = 1, y = 2, body: untyped = 3): auto = (x, y, astToStr(body))
|
||||||
|
doAssert compiles(fn5(1, 2, foo))
|
||||||
|
doAssert not compiles(fn5(1, foo))
|
||||||
|
|
||||||
|
block:
|
||||||
|
# with an overload
|
||||||
|
var witness = 0
|
||||||
|
template fn6() = discard
|
||||||
|
template fn6(procname: string, body: untyped): untyped = witness.inc
|
||||||
|
fn6("abc"): discard
|
||||||
|
assert witness == 1
|
||||||
|
|
||||||
|
block:
|
||||||
|
# with overloads
|
||||||
|
var witness = 0
|
||||||
|
template fn6() = discard
|
||||||
|
template fn6(a: int) = discard
|
||||||
|
template fn6(procname: string, body: untyped): untyped = witness.inc
|
||||||
|
fn6("abc"): discard
|
||||||
|
assert witness == 1
|
||||||
|
|
||||||
|
template fn6(b = 1.5, body: untyped): untyped = witness.inc
|
||||||
|
fn6(1.3): discard
|
||||||
|
assert witness == 2
|
||||||
|
|
||||||
|
block:
|
||||||
|
var witness = 0
|
||||||
|
template fn6(a: int) = discard
|
||||||
|
template fn6(a: string) = discard
|
||||||
|
template fn6(ignore: string, b = 1.5, body: untyped): untyped = witness.inc
|
||||||
|
fn6(""):
|
||||||
|
foobar1
|
||||||
|
foobar2
|
||||||
|
doAssert witness == 1
|
||||||
|
fn6(""): discard
|
||||||
|
doAssert witness == 2
|
||||||
|
|
||||||
|
block: # multi block args
|
||||||
|
template fn8(a = 1, b = 2, body1: untyped, body2: untyped): auto = (a, b, astToStr(body1), astToStr(body2))
|
||||||
|
let a1 = fn8():
|
||||||
|
foobar1
|
||||||
|
foobar2
|
||||||
|
do:
|
||||||
|
foobar3
|
||||||
|
foobar4
|
||||||
|
doAssert a1 == (1, 2, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4")
|
||||||
|
|
||||||
|
let a2 = fn8(b = 20):
|
||||||
|
foobar1
|
||||||
|
foobar2
|
||||||
|
do:
|
||||||
|
foobar3
|
||||||
|
foobar4
|
||||||
|
doAssert a2 == (1, 20, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4")
|
||||||
|
|
||||||
|
static: main()
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue