diff --git a/compiler/ast.nim b/compiler/ast.nim index 4001e896e3..25958f580b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -784,6 +784,7 @@ type tab*: TStrTable # interface table for modules of skLet, skVar, skField, skForVar: guard*: PSym + bitsize*: int else: nil magic*: TMagic typ*: PType diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 84d02d1da6..1ed9ce1139 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -441,6 +441,8 @@ proc genRecordFieldsAux(m: BModule, n: PNode, elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) + elif field.bitsize != 0: + addf(result, "$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)]) else: # don't use fieldType here because we need the # tyGenericInst for C++ template support diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 29aa03c94c..3882bdd036 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -14,7 +14,7 @@ import lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, - securehash + securehash, streams type TSystemCC* = enum @@ -672,6 +672,12 @@ proc callCCompiler*(projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] + let runCb = proc (idx: int, p: Process) = + let exitCode = p.peekExitCode + if exitCode != 0: + rawMessage(errGenerated, "execution of an external compiler program '" & + cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" & + p.outputStream.readAll.strip) compileCFile(toCompile, script, cmds, prettyCmds, false) compileCFile(externalToCompile, script, cmds, prettyCmds, true) if optCompileOnly notin gGlobalOptions: @@ -682,22 +688,17 @@ proc callCCompiler*(projectfile: string) = res = execWithEcho(cmds[i]) if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i]) elif optListCmd in gGlobalOptions or gVerbosity > 1: - res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams}, - gNumberOfProcessors) + res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) elif gVerbosity == 1: - res = execProcesses(cmds, {poUsePath, poParentStreams}, - gNumberOfProcessors, prettyCb) + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, prettyCb, afterRunEvent=runCb) else: - res = execProcesses(cmds, {poUsePath, poParentStreams}, - gNumberOfProcessors) + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) if res != 0: if gNumberOfProcessors <= 1: rawMessage(errExecutionOfProgramFailed, cmds.join()) - else: - rawMessage(errGenerated, - " execution of an external compiler program failed: " & - cmds.join() & "; " & - "rerun with --parallelBuild:1 to see the error message") if optNoLinking notin gGlobalOptions: # call the linker: var it = PStrEntry(toLink.head) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1c51251fe7..ba05b2792a 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -56,7 +56,7 @@ const wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wBorrow, wGcSafe} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wGuard} + wImportCpp, wImportObjC, wError, wGuard, wBitsize} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, @@ -844,6 +844,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, if sym == nil: pragmaLockStmt(c, it) elif sym.typ == nil: invalidPragma(it) else: sym.typ.lockLevel = pragmaLocks(c, it) + of wBitsize: + if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr: + invalidPragma(it) + else: + sym.bitsize = expectIntLit(c, it) of wGuard: if sym == nil or sym.kind notin {skVar, skLet, skField}: invalidPragma(it) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 23f012ea50..d1e5438f3d 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -82,6 +82,7 @@ type wStdIn, wStdOut, wStdErr, wInOut, wByCopy, wByRef, wOneWay, + wBitsize, TSpecialWords* = set[TSpecialWord] @@ -168,6 +169,7 @@ const "stdin", "stdout", "stderr", "inout", "bycopy", "byref", "oneway", + "bitsize", ] proc findStr*(a: openArray[string], s: string): int = diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index 68a88f865a..f89194c9ad 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -531,6 +531,24 @@ Implementation Specific Pragmas This section describes additional pragmas that the current Nim implementation supports but which should not be seen as part of the language specification. +Bitsize pragma +-------------- + +The ``bitsize`` pragma is for object field members. It declares the field as +a bitfield in C/C++. + +.. code-block:: Nim + type + mybitfield = object + flag {.bitsize:1.}: cuint + +generates: + +.. code-block:: C + struct mybitfield { + unsigned int flag:1; + }; + Volatile pragma --------------- diff --git a/examples/filterex.nim b/examples/filterex.nim index d9bfb67822..0839452544 100644 --- a/examples/filterex.nim +++ b/examples/filterex.nim @@ -1,4 +1,4 @@ -#! stdtmpl | standard +#? stdtmpl | standard #proc generateHTMLPage(title, currentTab, content: string, # tabs: openArray[string]): string = # result = "" diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index fd28db4438..667ca82d73 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -500,7 +500,7 @@ when defined(nimdoc) and not declared(os): proc existsFile(x: string): bool = discard when declared(getEnv) or defined(nimscript): - proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = + proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the home directory of the current user. ## ## This proc is wrapped by the expandTilde proc for the convenience of @@ -508,7 +508,7 @@ when declared(getEnv) or defined(nimscript): when defined(windows): return string(getEnv("USERPROFILE")) & "\\" else: return string(getEnv("HOME")) & "/" - proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = + proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the config directory of the current user for applications. when defined(windows): return string(getEnv("APPDATA")) & "\\" else: return string(getEnv("HOME")) & "/.config/" @@ -519,7 +519,7 @@ when declared(getEnv) or defined(nimscript): when defined(windows): return string(getEnv("TEMP")) & "\\" else: return "/tmp/" - proc expandTilde*(path: string): string {.tags: [ReadEnvEffect].} = + proc expandTilde*(path: string): string {.tags: [ReadEnvEffect, ReadIOEffect].} = ## Expands a path starting with ``~/`` to a full path. ## ## If `path` starts with the tilde character and is followed by `/` or `\\` @@ -549,7 +549,7 @@ when declared(getEnv) or defined(nimscript): yield substr(s, first, last-1) inc(last) - proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect].} = + proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} = ## Searches for `exe` in the current working directory and then ## in directories listed in the ``PATH`` environment variable. ## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe` diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index bc73f7119d..c23126be91 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -248,7 +248,8 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} = proc execProcesses*(cmds: openArray[string], options = {poStdErrToStdOut, poParentStreams}, n = countProcessors(), - beforeRunEvent: proc(idx: int) = nil): int + beforeRunEvent: proc(idx: int) = nil, + afterRunEvent: proc(idx: int, p: Process) = nil): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect]} = ## executes the commands `cmds` in parallel. Creates `n` processes @@ -278,6 +279,7 @@ proc execProcesses*(cmds: openArray[string], err.add("\n") echo(err) result = max(waitForExit(q[r]), result) + if afterRunEvent != nil: afterRunEvent(r, q[r]) if q[r] != nil: close(q[r]) if beforeRunEvent != nil: beforeRunEvent(i) @@ -291,6 +293,7 @@ proc execProcesses*(cmds: openArray[string], if not running(q[r]): #echo(outputStream(q[r]).readLine()) result = max(waitForExit(q[r]), result) + if afterRunEvent != nil: afterRunEvent(r, q[r]) if q[r] != nil: close(q[r]) if beforeRunEvent != nil: beforeRunEvent(i) @@ -299,6 +302,7 @@ proc execProcesses*(cmds: openArray[string], if i > high(cmds): break for j in 0..m-1: result = max(waitForExit(q[j]), result) + if afterRunEvent != nil: afterRunEvent(j, q[j]) if q[j] != nil: close(q[j]) else: for i in 0..high(cmds): @@ -306,6 +310,7 @@ proc execProcesses*(cmds: openArray[string], beforeRunEvent(i) var p = startProcess(cmds[i], options=options + {poEvalCommand}) result = max(waitForExit(p), result) + if afterRunEvent != nil: afterRunEvent(i, p) close(p) proc select*(readfds: var seq[Process], timeout = 500): int {.benign.} diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 60d09c71ac..7d9241412a 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -18,8 +18,9 @@ type Rational*[T] = object ## a rational number, consisting of a numerator and denominator num*, den*: T -proc initRational*[T](num, den: T): Rational[T] = +proc initRational*[T:SomeInteger](num, den: T): Rational[T] = ## Create a new rational number. + assert(den != 0, "a denominator of zero value is invalid") result.num = num result.den = den @@ -33,7 +34,7 @@ proc `$`*[T](x: Rational[T]): string = ## Turn a rational number into a string. result = $x.num & "/" & $x.den -proc toRational*[T](x: T): Rational[T] = +proc toRational*[T:SomeInteger](x: T): Rational[T] = ## Convert some integer `x` to a rational number. result.num = x result.den = 1 @@ -47,7 +48,7 @@ proc toInt*[T](x: Rational[T]): int = ## `x` does not contain an integer value. x.num div x.den -proc reduce*[T](x: var Rational[T]) = +proc reduce*[T:SomeInteger](x: var Rational[T]) = ## Reduce rational `x`. let common = gcd(x.num, x.den) if x.den > 0: diff --git a/tests/pragmas/tbitsize.nim b/tests/pragmas/tbitsize.nim new file mode 100644 index 0000000000..7a44944d24 --- /dev/null +++ b/tests/pragmas/tbitsize.nim @@ -0,0 +1,22 @@ +discard """ +ccodeCheck: "\\i @'unsigned int flag:1;' .*" +""" + +type + bits* = object + flag* {.bitsize: 1.}: cuint + opts* {.bitsize: 4.}: cint + +var + b: bits + +assert b.flag == 0 +b.flag = 1 +assert b.flag == 1 +b.flag = 2 +assert b.flag == 0 + +b.opts = 7 +assert b.opts == 7 +b.opts = 9 +assert b.opts == -7 diff --git a/tests/rational/trat_float.nim b/tests/rational/trat_float.nim new file mode 100644 index 0000000000..24797c4a01 --- /dev/null +++ b/tests/rational/trat_float.nim @@ -0,0 +1,9 @@ +discard """ + file: "trat_float.nim" + line: "9,19" + errormsg: '''type mismatch: got''' +""" +import rationals +var + # this fails - no floats as num or den + r = initRational(1.0'f, 1.0'f) diff --git a/tests/rational/trat_init.nim b/tests/rational/trat_init.nim new file mode 100644 index 0000000000..df29ff6e33 --- /dev/null +++ b/tests/rational/trat_init.nim @@ -0,0 +1,10 @@ +discard """ + file: "trat_init.nim" + exitcode: "1" +""" +import rationals +var + z = Rational[int](num: 0, den: 1) + o = initRational(num=1, den=1) + a = initRational(1, 2) + r = initRational(1, 0) # this fails - no zero denominator diff --git a/tests/template/sunset.tmpl b/tests/template/sunset.tmpl index 6475bac4ec..465b12a5ea 100644 --- a/tests/template/sunset.tmpl +++ b/tests/template/sunset.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl +#? stdtmpl #proc sunsetTemplate*(current, ticker, content: string, # tabs: openarray[array[0..1, string]]): string = # result = "" diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 3961f15c40..e52988682b 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -213,6 +213,8 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec; expectedmsg = expected.nimout givenmsg = given.nimout.strip nimoutCheck(test, expectedmsg, given) + else: + givenmsg = given.nimout.strip if given.err == reSuccess: inc(r.passed) r.addResult(test, expectedmsg, givenmsg, given.err) diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.tmpl index 2c6a2b5a80..2a8da144d2 100644 --- a/tools/niminst/buildbat.tmpl +++ b/tools/niminst/buildbat.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateBuildBatchScript(c: ConfigData, winIndex, cpuIndex: int): string = # result = "@echo off\nREM Generated by niminst\n" SET CC=gcc diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl index 52da351bee..463a1ad527 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateBuildShellScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated from niminst\n" & # "# Template is in tools/niminst/buildsh.tmpl\n" & diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.tmpl index c4717a2579..7349abcb43 100644 --- a/tools/niminst/deinstall.tmpl +++ b/tools/niminst/deinstall.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateDeinstallScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLower diff --git a/tools/niminst/inno.tmpl b/tools/niminst/inno.tmpl index 4acf0557c8..ef2da8a755 100644 --- a/tools/niminst/inno.tmpl +++ b/tools/niminst/inno.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl | standard +#? stdtmpl | standard #proc generateInnoSetup(c: ConfigData): string = # result = "" ; Default Template for NimInst diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.tmpl index 3ec42c2874..14d88e07df 100644 --- a/tools/niminst/install.tmpl +++ b/tools/niminst/install.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar = '?') | standard +#? stdtmpl(subsChar = '?') | standard #proc generateInstallScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLower diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl index 8ab3b89d15..6615ddc022 100644 --- a/tools/niminst/makefile.tmpl +++ b/tools/niminst/makefile.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateMakefile(c: ConfigData): string = # result = "# Generated from niminst\n" & # "# Template is in tools/niminst/makefile.tmpl\n" & diff --git a/tools/niminst/nsis.tmpl b/tools/niminst/nsis.tmpl index 843a8cf440..abf4623887 100644 --- a/tools/niminst/nsis.tmpl +++ b/tools/niminst/nsis.tmpl @@ -1,4 +1,4 @@ -#! stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateNsisSetup(c: ConfigData): string = # result = "; NSIS script generated by niminst\n" & # "; To regenerate run ``niminst nsis`` or ``koch nsis``\n" diff --git a/web/news.txt b/web/news.txt index cfa40c65ca..33ceac49e4 100644 --- a/web/news.txt +++ b/web/news.txt @@ -108,6 +108,8 @@ News - The compiler finally considers symbol binding rules in templates and generics for overloaded ``[]``, ``[]=``, ``{}``, ``{}=`` operators (issue `#2599 `_). + - The compiler now supports a `bitsize pragma `_ + for constructing bitfields. Language Additions