From 57ea01309eb7ff1425fb3bc7907c67139fc50edf Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 9 Mar 2017 17:09:39 +0100 Subject: nimsuggest: more things work --- compiler/semexprs.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a419cd000..3dc174527 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1054,7 +1054,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # here at all! #if isSymChoice(n.sons[1]): return when defined(nimsuggest): - if gCmd == cmdIdeTools: suggestExpr(c, n) + if gCmd == cmdIdeTools: + suggestExpr(c, n) + if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n) var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule}) if s != nil: @@ -2234,6 +2236,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1) + #when defined(nimsuggest): + # if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n) let mode = if nfDotField in n.flags: {} else: {checkUndeclared} var s = qualifiedLookUp(c, n.sons[0], mode) if s != nil: -- cgit 1.4.1-2-gfad0 From 6e358e318747ecd6bea66911d6144cb7eff9d172 Mon Sep 17 00:00:00 2001 From: zah Date: Sun, 12 Mar 2017 10:27:05 +0200 Subject: don't allow casting to non-concrete types; fixes #5428 (#5502) --- compiler/msgs.nim | 2 ++ compiler/semexprs.nim | 13 ++++++---- compiler/types.nim | 1 + tests/errmsgs/tnon_concrete_cast.nim | 47 ++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 tests/errmsgs/tnon_concrete_cast.nim (limited to 'compiler/semexprs.nim') diff --git a/compiler/msgs.nim b/compiler/msgs.nim index e50ed0f2a..bf9090089 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -89,6 +89,7 @@ type errMainModuleMustBeSpecified, errXExpected, errTIsNotAConcreteType, + errCastToANonConcreteType, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, @@ -326,6 +327,7 @@ const errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errXExpected: "\'$1\' expected", errTIsNotAConcreteType: "\'$1\' is not a concrete type.", + errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'", errInvalidSectionStart: "invalid section start", errGridTableNotImplemented: "grid table is not implemented", errGeneralParseError: "general parse error", diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a419cd000..755d44448 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -218,13 +218,16 @@ proc semConv(c: PContext, n: PNode): PNode = proc semCast(c: PContext, n: PNode): PNode = ## Semantically analyze a casting ("cast[type](param)") checkSonsLen(n, 2) + let targetType = semTypeNode(c, n.sons[0], nil) + let castedExpr = semExprWithType(c, n.sons[1]) + if tfHasMeta in targetType.flags: + localError(n.sons[0].info, errCastToANonConcreteType, $targetType) + if not isCastable(targetType, castedExpr.typ): + localError(n.info, errExprCannotBeCastToX, $targetType) result = newNodeI(nkCast, n.info) - result.typ = semTypeNode(c, n.sons[0], nil) + result.typ = targetType addSon(result, copyTree(n.sons[0])) - addSon(result, semExprWithType(c, n.sons[1])) - if not isCastable(result.typ, result.sons[1].typ): - localError(result.info, errExprCannotBeCastToX, - typeToString(result.typ)) + addSon(result, castedExpr) proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = const diff --git a/compiler/types.nim b/compiler/types.nim index df1d3e3ca..80fb6612d 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -20,6 +20,7 @@ type preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string +template `$`*(typ: PType): string = typeToString(typ) proc base*(t: PType): PType = result = t.sons[0] diff --git a/tests/errmsgs/tnon_concrete_cast.nim b/tests/errmsgs/tnon_concrete_cast.nim new file mode 100644 index 000000000..e4ae890ce --- /dev/null +++ b/tests/errmsgs/tnon_concrete_cast.nim @@ -0,0 +1,47 @@ +discard """ + errormsg: "cannot cast to a non concrete type: 'ptr SomeNumber'" + line: 36 +""" + +# https://github.com/nim-lang/Nim/issues/5428 + +type + MemFile = object + mem: pointer + +proc memfileopen(filename: string, newFileSize: int): MemFile = + # just a memfile mock + return + +type + MyData = object + member1: seq[int] + member2: int + +type + MyReadWrite = object + memfile: MemFile + offset: int + +# Here, SomeNumber is bound to a concrete type, and that's OK +proc write(rw: var MyReadWrite; value: SomeNumber): void = + (cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + rw.offset.uint))[] = value + rw.offset += sizeof(SomeNumber) + +# Here, we try to use SomeNumber without binding it to a type. This should +# produce an error message for now. It's also possible to relax the rules +# and allow for type-class based type inference in such situations. +proc write[T](rw: var MyReadWrite; value: seq[T]): void = + rw.write value.len + let dst = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset)) + let src = cast[pointer](value[0].unsafeAddr) + let size = sizeof(T) * value.len + copyMem(dst, src, size) + rw.offset += size + +proc saveBinFile(arg: var MyData, filename: string): void = + var rw: MyReadWrite + rw.memfile = memfileOpen(filename, newFileSize = rw.offset) + rw.offset = 0 + rw.write arg.member1 + -- cgit 1.4.1-2-gfad0 From 1be0022e7c6a8d168918998fd27412901432075d Mon Sep 17 00:00:00 2001 From: zah Date: Sun, 12 Mar 2017 10:33:49 +0200 Subject: 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. --- compiler/ast.nim | 2 ++ compiler/evaltempl.nim | 5 +++++ compiler/msgs.nim | 6 ++++++ compiler/sem.nim | 7 +++++++ compiler/semcall.nim | 1 + compiler/semexprs.nim | 2 ++ compiler/semstmts.nim | 2 ++ compiler/semtypes.nim | 5 ++++- compiler/sigmatch.nim | 1 + compiler/types.nim | 4 +++- tests/errmsgs/t5167_1.nim | 17 +++++++++++++++++ tests/errmsgs/t5167_2.nim | 12 ++++++++++++ tests/errmsgs/t5167_3.nim | 25 +++++++++++++++++++++++++ tests/errmsgs/t5167_4.nim | 20 ++++++++++++++++++++ tests/errmsgs/t5167_5.nim | 25 +++++++++++++++++++++++++ tests/macros/tmacro4.nim | 2 +- tests/macros/tquotewords.nim | 2 +- 17 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 tests/errmsgs/t5167_1.nim create mode 100644 tests/errmsgs/t5167_2.nim create mode 100644 tests/errmsgs/t5167_3.nim create mode 100644 tests/errmsgs/t5167_4.nim create mode 100644 tests/errmsgs/t5167_5.nim (limited to 'compiler/semexprs.nim') 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 = 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: -- cgit 1.4.1-2-gfad0