Fixes #5167 and related problems (#5475)

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:
zah 2017-03-12 10:33:49 +02:00 • committed by Andreas Rumpf
commit 1be0022e7c
17 changed files with 134 additions and 4 deletions

View file

@ -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)

View file

@ -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]

View file

@ -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\'",

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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
View 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
View 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
View 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
View 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
View 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

View file

@ -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"))

View file

@ -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: