diff options
author | zah <zahary@gmail.com> | 2017-03-12 10:33:49 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-03-12 09:33:49 +0100 |
commit | 1be0022e7c6a8d168918998fd27412901432075d (patch) | |
tree | 64be855cb15ffb466098e33b526799a6aa49a9c7 | |
parent | 6e358e318747ecd6bea66911d6144cb7eff9d172 (diff) | |
download | Nim-1be0022e7c6a8d168918998fd27412901432075d.tar.gz |
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.
-rw-r--r-- | compiler/ast.nim | 2 | ||||
-rw-r--r-- | compiler/evaltempl.nim | 5 | ||||
-rw-r--r-- | compiler/msgs.nim | 6 | ||||
-rw-r--r-- | compiler/sem.nim | 7 | ||||
-rw-r--r-- | compiler/semcall.nim | 1 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/semstmts.nim | 2 | ||||
-rw-r--r-- | compiler/semtypes.nim | 5 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 1 | ||||
-rw-r--r-- | compiler/types.nim | 4 | ||||
-rw-r--r-- | tests/errmsgs/t5167_1.nim | 17 | ||||
-rw-r--r-- | tests/errmsgs/t5167_2.nim | 12 | ||||
-rw-r--r-- | tests/errmsgs/t5167_3.nim | 25 | ||||
-rw-r--r-- | tests/errmsgs/t5167_4.nim | 20 | ||||
-rw-r--r-- | tests/errmsgs/t5167_5.nim | 25 | ||||
-rw-r--r-- | tests/macros/tmacro4.nim | 2 | ||||
-rw-r--r-- | tests/macros/tquotewords.nim | 2 |
17 files changed, 134 insertions, 4 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 66fbe577c..9d79620b2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -460,6 +460,8 @@ type # proc foo(T: typedesc, list: seq[T]): var T # proc foo(L: static[int]): array[L, int] # 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. tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 318254a80..5bd274a3e 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -80,9 +80,14 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = expectedRegularParams = <s.typ.len givenRegularParams = totalParams - genericParams if givenRegularParams < 0: givenRegularParams = 0 + if totalParams > expectedRegularParams + genericParams: globalError(n.info, errWrongNumberOfArguments) + if totalParams < genericParams: + globalError(n.info, errMissingGenericParamsForTemplate, + n.renderTree) + result = newNodeI(nkArgList, n.info) for i in 1 .. givenRegularParams: result.addSon n.sons[i] diff --git a/compiler/msgs.nim b/compiler/msgs.nim index bf9090089..eaaa0aaf3 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -64,6 +64,8 @@ type errVarForOutParamNeeded, errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, errAmbiguousCallXYZ, errWrongNumberOfArguments, + errWrongNumberOfArgumentsInCall, + errMissingGenericParamsForTemplate, errXCannotBePassedToProcVar, errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, @@ -108,6 +110,7 @@ type errCannotInferTypeOfTheLiteral, errCannotInferReturnType, errGenericLambdaNotAllowed, + errProcHasNoConcreteType, errCompilerDoesntSupportTarget, errUser, warnCannotOpenFile, @@ -270,6 +273,8 @@ const errButExpectedX: "but expected \'$1\'", errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", 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", errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", 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 " & "it is used as an operand to another routine and the types " & "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", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\'", diff --git a/compiler/sem.nim b/compiler/sem.nim index 21a5c435a..e1d18e61f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -381,6 +381,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, if sym == c.p.owner: 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: # c.evalContext = c.createEvalContext(emStatic) result = evalMacroCall(c.module, c.cache, n, nOrig, sym) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 98667b085..3a43c63b2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -411,6 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = let tm = typeRel(m, formal, arg, true) if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) + newInst.typ.flags.excl tfUnresolved markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(newInst, n.info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 755d44448..f1bf5d864 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # result = errorNode(c, n) if result.typ != nil: # 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) elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyVoid, c) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7c6e3af6d..45b75cb3e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -499,6 +499,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if hasEmpty(typ): localError(def.info, errCannotInferTypeOfTheLiteral, ($typ.kind).substr(2).toLowerAscii) + elif typ.kind == tyProc and tfUnresolved in typ.flags: + localError(def.info, errProcHasNoConcreteType, def.renderTree) else: if symkind == skLet: localError(a.info, errLetNeedsInit) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 83d0c83b2..7877a26a9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.sons[0] = r result.n.typ = r - if genericParams != nil: + if genericParams != nil and genericParams.len > 0: for n in genericParams: + if {sfUsed, sfAnon} * n.sym.flags == {}: + result.flags.incl tfUnresolved + if tfWildcard in n.sym.typ.flags: n.sym.kind = skType n.sym.typ.flags.excl tfWildcard diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index f2caab41f..587598d3e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1514,6 +1514,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}: copyCandidate(z, m) z.callee = arg.sons[i].typ + if tfUnresolved in z.callee.flags: continue z.calleeSym = arg.sons[i].sym #if arg.sons[i].sym.name.s == "cmp": # ggDebug = true diff --git a/compiler/types.nim b/compiler/types.nim index 80fb6612d..f4ef75094 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -548,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if prefer != preferExported: result.add("(" & typeToString(t.sons[0]) & ")") 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): if t.n != nil and i < t.n.len and t.n[i].kind == nkSym: add(result, t.n[i].sym.name.s) diff --git a/tests/errmsgs/t5167_1.nim b/tests/errmsgs/t5167_1.nim new file mode 100644 index 000000000..9f4f208a4 --- /dev/null +++ b/tests/errmsgs/t5167_1.nim @@ -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 + diff --git a/tests/errmsgs/t5167_2.nim b/tests/errmsgs/t5167_2.nim new file mode 100644 index 000000000..17d96ef47 --- /dev/null +++ b/tests/errmsgs/t5167_2.nim @@ -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) + diff --git a/tests/errmsgs/t5167_3.nim b/tests/errmsgs/t5167_3.nim new file mode 100644 index 000000000..2781d3943 --- /dev/null +++ b/tests/errmsgs/t5167_3.nim @@ -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 + diff --git a/tests/errmsgs/t5167_4.nim b/tests/errmsgs/t5167_4.nim new file mode 100644 index 000000000..3d77fae02 --- /dev/null +++ b/tests/errmsgs/t5167_4.nim @@ -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 + diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim new file mode 100644 index 000000000..ab02f29f6 --- /dev/null +++ b/tests/errmsgs/t5167_5.nim @@ -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 + diff --git a/tests/macros/tmacro4.nim b/tests/macros/tmacro4.nim index a56369369..fb07941a9 100644 --- a/tests/macros/tmacro4.nim +++ b/tests/macros/tmacro4.nim @@ -5,7 +5,7 @@ discard """ import macros, strutils -macro test_macro*(n: stmt): stmt {.immediate.} = +macro test_macro*(s: string, n: stmt): stmt {.immediate.} = result = newNimNode(nnkStmtList) var ass : NimNode = newNimNode(nnkAsgn) add(ass, newIdentNode("str")) diff --git a/tests/macros/tquotewords.nim b/tests/macros/tquotewords.nim index 7a575f541..48fcafd62 100644 --- a/tests/macros/tquotewords.nim +++ b/tests/macros/tquotewords.nim @@ -6,7 +6,7 @@ discard """ import macros -macro quoteWords(n: expr): expr {.immediate.} = +macro quoteWords(n: varargs[expr]): expr {.immediate.} = let n = callsite() result = newNimNode(nnkBracket, n) for i in 1..n.len-1: |