diff options
-rw-r--r-- | .travis.yml | 10 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 8 | ||||
-rw-r--r-- | compiler/pragmas.nim | 28 | ||||
-rw-r--r-- | compiler/trees.nim | 2 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 4 | ||||
-rw-r--r-- | doc/manual/locking.txt | 19 | ||||
-rw-r--r-- | doc/manual/pragmas.txt | 32 | ||||
-rw-r--r-- | lib/pure/json.nim | 9 | ||||
-rw-r--r-- | lib/pure/os.nim | 2 | ||||
-rw-r--r-- | tests/ccgbugs/tmangle_field.nim | 16 | ||||
-rw-r--r-- | tests/pragmas/tlocks.nim | 13 | ||||
-rw-r--r-- | tests/pragmas/tused.nim | 13 | ||||
-rw-r--r-- | web/news/e031_version_0_16_2.rst | 4 |
13 files changed, 135 insertions, 25 deletions
diff --git a/.travis.yml b/.travis.yml index a3db0c58e..ffb0033a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,24 @@ sudo: false language: c os: - linux + - osx dist: trusty +matrix: + allow_failures: + - os: osx + addons: apt: packages: - libcurl4-openssl-dev - libsdl1.2-dev - libgc-dev + brew: + packages: + - boehmgc + - node + before_script: - set -e - curl --out fasm-1.71.39.tgz https://nim-lang.org/download/fasm-1.71.39.tgz diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index e30fe5598..29d4e23c9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -24,7 +24,13 @@ proc isKeyword(w: PIdent): bool = proc mangleField(m: BModule; name: PIdent): string = result = mangle(name.s) - if isKeyword(name) or m.g.config.cppDefines.contains(result): + # fields are tricky to get right and thanks to generic types producing + # duplicates we can end up mangling the same field multiple times. However + # if we do so, the 'cppDefines' table might be modified in the meantime + # meaning we produce inconsistent field names (see bug #5404). + # Hence we do not check for ``m.g.config.cppDefines.contains(result)`` here + # anymore: + if isKeyword(name): result.add "_0" when false: diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 04dbd3612..bcb0461f2 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -25,19 +25,19 @@ const wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, - wOverride, wConstructor, wExportNims} + wOverride, wConstructor, wExportNims, wUsed} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator, wExportNims} + wDelegator, wExportNims, wUsed} macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator, - wExportNims} + wExportNims, wUsed} iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, - wTags, wLocks, wGcSafe, wExportNims} + wTags, wLocks, wGcSafe, wExportNims, wUsed} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, @@ -55,16 +55,16 @@ const wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wBorrow, wGcSafe, wExportNims, wPartial} + wBorrow, wGcSafe, wExportNims, wPartial, wUsed} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wGuard, wBitsize} + wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims} + wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, - wIntDefine, wStrDefine} + wIntDefine, wStrDefine, wUsed} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} @@ -582,7 +582,13 @@ proc pragmaLocks(c: PContext, it: PNode): TLockLevel = if it.kind != nkExprColonExpr: invalidPragma(it) else: - if it[1].kind != nkNilLit: + case it[1].kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + if it[1].strVal == "unknown": + result = UnknownLockLevel + else: + localError(it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")") + else: let x = expectIntLit(c, it) if x < 0 or x > MaxLockLevel: localError(it[1].info, "integer must be within 0.." & $MaxLockLevel) @@ -961,6 +967,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, sym.magic = mIntDefine of wStrDefine: sym.magic = mStrDefine + of wUsed: + noVal(it) + if sym == nil: invalidPragma(it) + else: sym.flags.incl sfUsed else: invalidPragma(it) else: invalidPragma(it) diff --git a/compiler/trees.nim b/compiler/trees.nim index 79a460aa0..424fba14c 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -62,7 +62,7 @@ proc sameTree*(a, b: PNode): bool = # don't go nuts here: same symbol as string is enough: result = a.sym.name.id == b.sym.name.id of nkIdent: result = a.ident.id == b.ident.id - of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal + of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkEmpty, nkNilLit, nkType: result = true diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index cf66b6358..6072bd64c 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -45,7 +45,7 @@ type wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit, wAlign, wNodecl, wPure, wSideeffect, wHeader, wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib, - wCompilerproc, wProcVar, wBase, + wCompilerproc, wProcVar, wBase, wUsed, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wLink, wCompile, wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger, @@ -131,7 +131,7 @@ const "incompletestruct", "requiresinit", "align", "nodecl", "pure", "sideeffect", "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib", - "compilerproc", "procvar", "base", + "compilerproc", "procvar", "base", "used", "fatal", "error", "warning", "hint", "line", "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", "link", "compile", "linksys", "deprecated", "varargs", diff --git a/doc/manual/locking.txt b/doc/manual/locking.txt index c00efdd91..c1bd5ca46 100644 --- a/doc/manual/locking.txt +++ b/doc/manual/locking.txt @@ -198,3 +198,22 @@ This is essential so that procs can be called within a ``locks`` section: As usual ``locks`` is an inferred effect and there is a subtype relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}`` iff (M <= N). + +The ``locks`` pragma can also take the special value ``"unknown"``. This +is useful in the context of dynamic method dispatching. In the following +example, the compiler can infer a lock level of 0 for the ``base`` case. +However, one of the overloaded methods calls a procvar which is +potentially locking. Thus, the lock level of calling ``g.testMethod`` +cannot be inferred statically, leading to compiler warnings. By using +``{.locks: "unknown".}``, the base method can be marked explicitly as +having unknown lock level as well: + +.. code-block:: nim + type SomeBase* = ref object of RootObj + type SomeDerived* = ref object of SomeBase + memberProc*: proc () + + method testMethod(g: SomeBase) {.base, locks: "unknown".} = discard + method testMethod(g: SomeDerived) = + if g.memberProc != nil: + g.memberProc() diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index 2a276c2e7..d30c37ff7 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -503,6 +503,26 @@ identifier that can be used to enable or disable it: This is often better than disabling all warnings at once. +used pragma +----------- + +Nim produces a warning for symbols that are not exported and not used either. +The ``used`` pragma can be attached to a symbol to suppress this warning. This +is particularly useful when the symbol was generated by a macro: + +.. code-block:: nim + template implementArithOps(T) = + proc echoAdd(a, b: T) {.used.} = + echo a + b + proc echoSub(a, b: T) {.used.} = + echo a - b + + # no warning produced for the unused 'echoSub' + implementArithOps(int) + echoAdd 3, 5 + + + experimental pragma ------------------- @@ -1018,12 +1038,12 @@ the -d/--define option at compile time. The implementation currently provides the following possible options (various others may be added later). -=============== ============================================ -pragma description -=============== ============================================ -intdefine Reads in a build-time define as an integer -strdefine Reads in a build-time define as a string -=============== ============================================ +================= ============================================ +pragma description +================= ============================================ +`intdefine`:idx: Reads in a build-time define as an integer +`strdefine`:idx: Reads in a build-time define as a string +================= ============================================ .. code-block:: nim const FooBar {.intdefine.}: int = 5 diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 5e36a2aa1..c7b581a85 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -952,7 +952,7 @@ proc newIndent(curr, indent: int, ml: bool): int = else: return indent proc nl(s: var string, ml: bool) = - if ml: s.add("\n") + s.add(if ml: "\n" else: " ") proc escapeJson*(s: string; result: var string) = ## Converts a string `s` to its JSON representation. @@ -986,15 +986,14 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, lstArr = false, currIndent = 0) = case node.kind of JObject: - if currIndent != 0 and not lstArr: result.nl(ml) - result.indent(currIndent) # Indentation + if lstArr: result.indent(currIndent) # Indentation if node.fields.len > 0: result.add("{") result.nl(ml) # New line var i = 0 for key, val in pairs(node.fields): if i > 0: - result.add(", ") + result.add(",") result.nl(ml) # New Line inc i # Need to indent more than { @@ -1030,7 +1029,7 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, result.nl(ml) for i in 0..len(node.elems)-1: if i > 0: - result.add(", ") + result.add(",") result.nl(ml) # New Line toPretty(result, node.elems[i], indent, ml, true, newIndent(currIndent, indent, ml)) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 92e295820..6bf776a44 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1185,7 +1185,7 @@ proc createHardlink*(src, dest: string) = proc parseCmdLine*(c: string): seq[string] {. noSideEffect, rtl, extern: "nos$1".} = - ## Splits a command line into several components; + ## Splits a `command line`:idx: into several components; ## This proc is only occasionally useful, better use the `parseopt` module. ## ## On Windows, it uses the following parsing rules diff --git a/tests/ccgbugs/tmangle_field.nim b/tests/ccgbugs/tmangle_field.nim new file mode 100644 index 000000000..9e4012b8b --- /dev/null +++ b/tests/ccgbugs/tmangle_field.nim @@ -0,0 +1,16 @@ +discard """ +""" + +# bug #5404 + +import parseopt2 + +{.emit: """typedef struct { + int key; +} foo;""".} + +type foo* {.importc: "foo", nodecl.} = object + key* {.importc: "key".}: cint + +for kind, key, value in parseopt2.getopt(): + discard diff --git a/tests/pragmas/tlocks.nim b/tests/pragmas/tlocks.nim new file mode 100644 index 000000000..ba66a2dca --- /dev/null +++ b/tests/pragmas/tlocks.nim @@ -0,0 +1,13 @@ + +type SomeBase* = ref object of RootObj +type SomeDerived* = ref object of SomeBase + memberProc*: proc () + +method testMethod(g: SomeBase) {.base, locks: "unknown".} = discard +method testMethod(g: SomeDerived) = + if g.memberProc != nil: + g.memberProc() + +# ensure int literals still work +proc plain*() {.locks: 0.} = + discard diff --git a/tests/pragmas/tused.nim b/tests/pragmas/tused.nim new file mode 100644 index 000000000..f3126bd45 --- /dev/null +++ b/tests/pragmas/tused.nim @@ -0,0 +1,13 @@ +discard """ + output: '''8''' +""" + +template implementArithOps(T) = + proc echoAdd(a, b: T) {.used.} = + echo a + b + proc echoSub(a, b: T) {.used.} = + echo a - b + +# no warning produced for the unused 'echoSub' +implementArithOps(int) +echoAdd 3, 5 diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 171e4cef1..785285eaf 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -66,6 +66,10 @@ these procedures. In the near future we will be converting all exception types to refs to remove the need for the ``newException`` template. +- A new pragma ``.used`` can be used for symbols to prevent +the "declared but not used" warning. More details can be found `here <http://nim-lang.org/docs/manual.html#pragmas-used-pragma>`_. + + Bugfixes -------- |