This commit returns to a bit less strict checking of the number of macro arguments, because some old immediate macros rely on a behavior where even the arity of the macro is not being checked. It may be better if such macros are just declared to use varargs[expr], but this remains for another day.
This commit is contained in:
parent
6e358e3187
commit
1be0022e7c
17 changed files with 134 additions and 4 deletions
|
|
@ -460,6 +460,8 @@ type
|
||||||
# proc foo(T: typedesc, list: seq[T]): var T
|
# proc foo(T: typedesc, list: seq[T]): var T
|
||||||
# proc foo(L: static[int]): array[L, int]
|
# proc foo(L: static[int]): array[L, int]
|
||||||
# can be attached to ranges to indicate that the range
|
# can be attached to ranges to indicate that the range
|
||||||
|
# can be attached to generic procs with free standing
|
||||||
|
# type parameters: e.g. proc foo[T]()
|
||||||
# depends on unresolved static params.
|
# depends on unresolved static params.
|
||||||
tfRetType, # marks return types in proc (used to detect type classes
|
tfRetType, # marks return types in proc (used to detect type classes
|
||||||
# used as return types for return type inference)
|
# used as return types for return type inference)
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,14 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
|
||||||
expectedRegularParams = <s.typ.len
|
expectedRegularParams = <s.typ.len
|
||||||
givenRegularParams = totalParams - genericParams
|
givenRegularParams = totalParams - genericParams
|
||||||
if givenRegularParams < 0: givenRegularParams = 0
|
if givenRegularParams < 0: givenRegularParams = 0
|
||||||
|
|
||||||
if totalParams > expectedRegularParams + genericParams:
|
if totalParams > expectedRegularParams + genericParams:
|
||||||
globalError(n.info, errWrongNumberOfArguments)
|
globalError(n.info, errWrongNumberOfArguments)
|
||||||
|
|
||||||
|
if totalParams < genericParams:
|
||||||
|
globalError(n.info, errMissingGenericParamsForTemplate,
|
||||||
|
n.renderTree)
|
||||||
|
|
||||||
result = newNodeI(nkArgList, n.info)
|
result = newNodeI(nkArgList, n.info)
|
||||||
for i in 1 .. givenRegularParams:
|
for i in 1 .. givenRegularParams:
|
||||||
result.addSon n.sons[i]
|
result.addSon n.sons[i]
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ type
|
||||||
errVarForOutParamNeeded,
|
errVarForOutParamNeeded,
|
||||||
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
|
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
|
||||||
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
||||||
|
errWrongNumberOfArgumentsInCall,
|
||||||
|
errMissingGenericParamsForTemplate,
|
||||||
errXCannotBePassedToProcVar,
|
errXCannotBePassedToProcVar,
|
||||||
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
|
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
|
||||||
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
|
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
|
||||||
|
|
@ -108,6 +110,7 @@ type
|
||||||
errCannotInferTypeOfTheLiteral,
|
errCannotInferTypeOfTheLiteral,
|
||||||
errCannotInferReturnType,
|
errCannotInferReturnType,
|
||||||
errGenericLambdaNotAllowed,
|
errGenericLambdaNotAllowed,
|
||||||
|
errProcHasNoConcreteType,
|
||||||
errCompilerDoesntSupportTarget,
|
errCompilerDoesntSupportTarget,
|
||||||
errUser,
|
errUser,
|
||||||
warnCannotOpenFile,
|
warnCannotOpenFile,
|
||||||
|
|
@ -270,6 +273,8 @@ const
|
||||||
errButExpectedX: "but expected \'$1\'",
|
errButExpectedX: "but expected \'$1\'",
|
||||||
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
|
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
|
||||||
errWrongNumberOfArguments: "wrong number of arguments",
|
errWrongNumberOfArguments: "wrong number of arguments",
|
||||||
|
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
|
||||||
|
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
|
||||||
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
|
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
|
||||||
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
|
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
|
||||||
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
|
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
|
||||||
|
|
@ -371,6 +376,7 @@ const
|
||||||
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
||||||
"it is used as an operand to another routine and the types " &
|
"it is used as an operand to another routine and the types " &
|
||||||
"of the generic paramers can be inferred from the expected signature.",
|
"of the generic paramers can be inferred from the expected signature.",
|
||||||
|
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
|
||||||
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
warnCannotOpenFile: "cannot open \'$1\'",
|
warnCannotOpenFile: "cannot open \'$1\'",
|
||||||
|
|
|
||||||
|
|
@ -381,6 +381,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||||
if sym == c.p.owner:
|
if sym == c.p.owner:
|
||||||
globalError(n.info, errRecursiveDependencyX, sym.name.s)
|
globalError(n.info, errRecursiveDependencyX, sym.name.s)
|
||||||
|
|
||||||
|
let genericParams = if sfImmediate in sym.flags: 0
|
||||||
|
else: sym.ast[genericParamsPos].len
|
||||||
|
let suppliedParams = max(n.safeLen - 1, 0)
|
||||||
|
|
||||||
|
if suppliedParams < genericParams:
|
||||||
|
globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree)
|
||||||
|
|
||||||
#if c.evalContext == nil:
|
#if c.evalContext == nil:
|
||||||
# c.evalContext = c.createEvalContext(emStatic)
|
# c.evalContext = c.createEvalContext(emStatic)
|
||||||
result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
|
result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
|
||||||
|
|
|
||||||
|
|
@ -411,6 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
let tm = typeRel(m, formal, arg, true)
|
let tm = typeRel(m, formal, arg, true)
|
||||||
if tm in {isNone, isConvertible}: return nil
|
if tm in {isNone, isConvertible}: return nil
|
||||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||||
|
newInst.typ.flags.excl tfUnresolved
|
||||||
markUsed(n.info, s, c.graph.usageSym)
|
markUsed(n.info, s, c.graph.usageSym)
|
||||||
styleCheckUse(n.info, s)
|
styleCheckUse(n.info, s)
|
||||||
result = newSymNode(newInst, n.info)
|
result = newSymNode(newInst, n.info)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# result = errorNode(c, n)
|
# result = errorNode(c, n)
|
||||||
if result.typ != nil:
|
if result.typ != nil:
|
||||||
# XXX tyGenericInst here?
|
# XXX tyGenericInst here?
|
||||||
|
if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
|
||||||
|
localError(n.info, errProcHasNoConcreteType, n.renderTree)
|
||||||
if result.typ.kind == tyVar: result = newDeref(result)
|
if result.typ.kind == tyVar: result = newDeref(result)
|
||||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||||
result.typ = newTypeS(tyVoid, c)
|
result.typ = newTypeS(tyVoid, c)
|
||||||
|
|
|
||||||
|
|
@ -499,6 +499,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
if hasEmpty(typ):
|
if hasEmpty(typ):
|
||||||
localError(def.info, errCannotInferTypeOfTheLiteral,
|
localError(def.info, errCannotInferTypeOfTheLiteral,
|
||||||
($typ.kind).substr(2).toLowerAscii)
|
($typ.kind).substr(2).toLowerAscii)
|
||||||
|
elif typ.kind == tyProc and tfUnresolved in typ.flags:
|
||||||
|
localError(def.info, errProcHasNoConcreteType, def.renderTree)
|
||||||
else:
|
else:
|
||||||
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
result.sons[0] = r
|
result.sons[0] = r
|
||||||
result.n.typ = r
|
result.n.typ = r
|
||||||
|
|
||||||
if genericParams != nil:
|
if genericParams != nil and genericParams.len > 0:
|
||||||
for n in genericParams:
|
for n in genericParams:
|
||||||
|
if {sfUsed, sfAnon} * n.sym.flags == {}:
|
||||||
|
result.flags.incl tfUnresolved
|
||||||
|
|
||||||
if tfWildcard in n.sym.typ.flags:
|
if tfWildcard in n.sym.typ.flags:
|
||||||
n.sym.kind = skType
|
n.sym.kind = skType
|
||||||
n.sym.typ.flags.excl tfWildcard
|
n.sym.typ.flags.excl tfWildcard
|
||||||
|
|
|
||||||
|
|
@ -1514,6 +1514,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||||
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
|
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||||
copyCandidate(z, m)
|
copyCandidate(z, m)
|
||||||
z.callee = arg.sons[i].typ
|
z.callee = arg.sons[i].typ
|
||||||
|
if tfUnresolved in z.callee.flags: continue
|
||||||
z.calleeSym = arg.sons[i].sym
|
z.calleeSym = arg.sons[i].sym
|
||||||
#if arg.sons[i].sym.name.s == "cmp":
|
#if arg.sons[i].sym.name.s == "cmp":
|
||||||
# ggDebug = true
|
# ggDebug = true
|
||||||
|
|
|
||||||
|
|
@ -548,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
if prefer != preferExported:
|
if prefer != preferExported:
|
||||||
result.add("(" & typeToString(t.sons[0]) & ")")
|
result.add("(" & typeToString(t.sons[0]) & ")")
|
||||||
of tyProc:
|
of tyProc:
|
||||||
result = if tfIterator in t.flags: "iterator (" else: "proc ("
|
result = if tfIterator in t.flags: "iterator " else: "proc "
|
||||||
|
if tfUnresolved in t.flags: result.add "[*missing parameters*]"
|
||||||
|
result.add "("
|
||||||
for i in countup(1, sonsLen(t) - 1):
|
for i in countup(1, sonsLen(t) - 1):
|
||||||
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
||||||
add(result, t.n[i].sym.name.s)
|
add(result, t.n[i].sym.name.s)
|
||||||
|
|
|
||||||
17
tests/errmsgs/t5167_1.nim
Normal file
17
tests/errmsgs/t5167_1.nim
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "'bar' doesn't have a concrete type, due to unspecified generic parameters."
|
||||||
|
line: 16
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc foo[T]() =
|
||||||
|
var y1 = foo[string]
|
||||||
|
var y2 = foo[T]
|
||||||
|
|
||||||
|
proc bar[T]() =
|
||||||
|
let x = 0
|
||||||
|
|
||||||
|
let good1 = foo[int]
|
||||||
|
let good2 = bar[int]
|
||||||
|
|
||||||
|
let err = bar
|
||||||
|
|
||||||
12
tests/errmsgs/t5167_2.nim
Normal file
12
tests/errmsgs/t5167_2.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c --threads:on $file"
|
||||||
|
errormsg: "'threadFunc' doesn't have a concrete type, due to unspecified generic parameters."
|
||||||
|
line: 11
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc threadFunc[T]() {.thread.} =
|
||||||
|
let x = 0
|
||||||
|
|
||||||
|
var thr: Thread[void]
|
||||||
|
thr.createThread(threadFunc)
|
||||||
|
|
||||||
25
tests/errmsgs/t5167_3.nim
Normal file
25
tests/errmsgs/t5167_3.nim
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c --threads:on $file"
|
||||||
|
errormsg: "type mismatch"
|
||||||
|
line: 24
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
TGeneric[T] = object
|
||||||
|
x: int
|
||||||
|
|
||||||
|
proc foo1[A, B, C, D](x: proc (a: A, b: B, c: C, d: D)) =
|
||||||
|
echo "foo1"
|
||||||
|
|
||||||
|
proc foo2(x: proc(x: int)) =
|
||||||
|
echo "foo2"
|
||||||
|
|
||||||
|
# The goal of this test is to verify that none of the generic parameters of the
|
||||||
|
# proc will be marked as unused. The error message should be "type mismatch" instead
|
||||||
|
# of "'bar' doesn't have a concrete type, due to unspecified generic parameters".
|
||||||
|
proc bar[A, B, C, D](x: A, y: seq[B], z: array[4, TGeneric[C]], r: TGeneric[D]) =
|
||||||
|
echo "bar"
|
||||||
|
|
||||||
|
foo1[int, seq[int], array[4, TGeneric[float]], TGeneric[string]] bar
|
||||||
|
foo2 bar
|
||||||
|
|
||||||
20
tests/errmsgs/t5167_4.nim
Normal file
20
tests/errmsgs/t5167_4.nim
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "type mismatch: got (proc [*missing parameters*](x: int) | proc (x: string){.gcsafe, locks: 0.})"
|
||||||
|
line: 19
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
TGeneric[T] = object
|
||||||
|
x: int
|
||||||
|
|
||||||
|
proc foo[B](x: int) =
|
||||||
|
echo "foo1"
|
||||||
|
|
||||||
|
proc foo(x: string) =
|
||||||
|
echo "foo2"
|
||||||
|
|
||||||
|
proc bar(x: proc (x: int)) =
|
||||||
|
echo "bar"
|
||||||
|
|
||||||
|
bar foo
|
||||||
|
|
||||||
25
tests/errmsgs/t5167_5.nim
Normal file
25
tests/errmsgs/t5167_5.nim
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim check $file"
|
||||||
|
errormsg: "'m' has unspecified generic parameters"
|
||||||
|
nimout: '''
|
||||||
|
t5167_5.nim(20, 9) Error: 't' has unspecified generic parameters
|
||||||
|
t5167_5.nim(21, 5) Error: 't' has unspecified generic parameters
|
||||||
|
t5167_5.nim(23, 9) Error: 'm' has unspecified generic parameters
|
||||||
|
t5167_5.nim(24, 5) Error: 'm' has unspecified generic parameters
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
template t[B]() =
|
||||||
|
echo "foo1"
|
||||||
|
|
||||||
|
macro m[T]: stmt = nil
|
||||||
|
|
||||||
|
proc bar(x: proc (x: int)) =
|
||||||
|
echo "bar"
|
||||||
|
|
||||||
|
let x = t
|
||||||
|
bar t
|
||||||
|
|
||||||
|
let y = m
|
||||||
|
bar m
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@ discard """
|
||||||
import
|
import
|
||||||
macros, strutils
|
macros, strutils
|
||||||
|
|
||||||
macro test_macro*(n: stmt): stmt {.immediate.} =
|
macro test_macro*(s: string, n: stmt): stmt {.immediate.} =
|
||||||
result = newNimNode(nnkStmtList)
|
result = newNimNode(nnkStmtList)
|
||||||
var ass : NimNode = newNimNode(nnkAsgn)
|
var ass : NimNode = newNimNode(nnkAsgn)
|
||||||
add(ass, newIdentNode("str"))
|
add(ass, newIdentNode("str"))
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ discard """
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
macro quoteWords(n: expr): expr {.immediate.} =
|
macro quoteWords(n: varargs[expr]): expr {.immediate.} =
|
||||||
let n = callsite()
|
let n = callsite()
|
||||||
result = newNimNode(nnkBracket, n)
|
result = newNimNode(nnkBracket, n)
|
||||||
for i in 1..n.len-1:
|
for i in 1..n.len-1:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue