renamed optional to discardable

This commit is contained in:
Araq 2011-09-24 19:18:08 +02:00
commit 485c371942
15 changed files with 72 additions and 63 deletions

View file

@ -227,7 +227,7 @@ type
# for interfacing with C++, JS # for interfacing with C++, JS
sfNamedParamCall, # symbol needs named parameter call syntax in target sfNamedParamCall, # symbol needs named parameter call syntax in target
# language; for interfacing with Objective C # language; for interfacing with Objective C
sfOptional # returned value may be discarded implicitely sfDiscardable # returned value may be discarded implicitely
TSymFlags* = set[TSymFlag] TSymFlags* = set[TSymFlag]

View file

@ -23,15 +23,15 @@ const
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame, wError, wOptional} wNoStackFrame, wError, wDiscardable}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
wImportcpp, wImportobjc, wError, wOptional} wImportcpp, wImportobjc, wError, wDiscardable}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect, iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportcpp, wImportobjc, wError, wOptional} wImportcpp, wImportobjc, wError, wDiscardable}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir,
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
@ -553,9 +553,9 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
of wPragma: of wPragma:
processPragma(c, n, i) processPragma(c, n, i)
break break
of wOptional: of wDiscardable:
noVal(it) noVal(it)
if sym != nil: incl(sym.flags, sfOptional) if sym != nil: incl(sym.flags, sfDiscardable)
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wByRef, wLinedir, wStacktrace, wLinetrace, wOptimization, wByRef,

View file

@ -550,7 +550,7 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
proc semExprNoType(c: PContext, n: PNode): PNode = proc semExprNoType(c: PContext, n: PNode): PNode =
proc ImplicitelyDiscardable(n: PNode): bool {.inline.} = proc ImplicitelyDiscardable(n: PNode): bool {.inline.} =
result = isCallExpr(n) and n.sons[0].kind == nkSym and result = isCallExpr(n) and n.sons[0].kind == nkSym and
sfOptional in n.sons[0].sym.flags sfDiscardable in n.sons[0].sym.flags
result = semExpr(c, n) result = semExpr(c, n)
if result.typ != nil and result.typ.kind != tyStmt: if result.typ != nil and result.typ.kind != tyStmt:
if gCmd == cmdInteractive: if gCmd == cmdInteractive:

View file

@ -51,7 +51,7 @@ type
wDeadCodeElim, wSafecode, wDeadCodeElim, wSafecode,
wPragma, wPragma,
wCompileTime, wCompileTime,
wPassc, wPassl, wBorrow, wOptional, wPassc, wPassl, wBorrow, wDiscardable,
wFieldChecks, wFieldChecks,
wCheckPoint, wSubsChar, wCheckPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wAcyclic, wShallow, wUnroll, wLinearScanEnd,
@ -96,7 +96,7 @@ const
"deadcodeelim", "safecode", "deadcodeelim", "safecode",
"pragma", "pragma",
"compiletime", "compiletime",
"passc", "passl", "borrow", "optional", "fieldchecks", "passc", "passl", "borrow", "discardable", "fieldchecks",
"checkpoint", "checkpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend", "subschar", "acyclic", "shallow", "unroll", "linearscanend",
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit", "write", "putenv", "prependenv", "appendenv", "threadvar", "emit",

View file

@ -1486,10 +1486,10 @@ Ignoring the return value of a procedure without using a discard statement is
a static error. a static error.
The return value can be ignored implicitely if the called proc/iterator has The return value can be ignored implicitely if the called proc/iterator has
been declared with the `optional`:idx: pragma: been declared with the `discardable`:idx: pragma:
.. code-block:: nimrod .. code-block:: nimrod
proc p(x, y: int): int {.optional.} = proc p(x, y: int): int {.discardable.} =
return x + y return x + y
p(3, 4) # now valid p(3, 4) # now valid

View file

@ -30,7 +30,7 @@ type
pos: int pos: int
inShortState: bool inShortState: bool
kind*: TCmdLineKind ## the dected command line token kind*: TCmdLineKind ## the dected command line token
key*, val*: string ## key and value pair; ``key`` is the option key*, val*: TaintedString ## key and value pair; ``key`` is the option
## or the argument, ``value`` is not "" if ## or the argument, ``value`` is not "" if
## the option was given a value ## the option was given a value
@ -50,8 +50,8 @@ when defined(os.ParamCount):
for i in countup(1, ParamCount()): for i in countup(1, ParamCount()):
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i).string) & ' ' result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i).string) & ' '
result.kind = cmdEnd result.kind = cmdEnd
result.key = "" result.key = TaintedString""
result.val = "" result.val = TaintedString""
proc parseWord(s: string, i: int, w: var string, proc parseWord(s: string, i: int, w: var string,
delim: TCharSet = {'\x09', ' ', '\0'}): int = delim: TCharSet = {'\x09', ' ', '\0'}): int =
@ -70,7 +70,7 @@ proc parseWord(s: string, i: int, w: var string,
proc handleShortOption(p: var TOptParser) = proc handleShortOption(p: var TOptParser) =
var i = p.pos var i = p.pos
p.kind = cmdShortOption p.kind = cmdShortOption
add(p.key, p.cmd[i]) add(p.key.string, p.cmd[i])
inc(i) inc(i)
p.inShortState = true p.inShortState = true
while p.cmd[i] in {'\x09', ' '}: while p.cmd[i] in {'\x09', ' '}:
@ -80,7 +80,7 @@ proc handleShortOption(p: var TOptParser) =
inc(i) inc(i)
p.inShortState = false p.inShortState = false
while p.cmd[i] in {'\x09', ' '}: inc(i) while p.cmd[i] in {'\x09', ' '}: inc(i)
i = parseWord(p.cmd, i, p.val) i = parseWord(p.cmd, i, p.val.string)
if p.cmd[i] == '\0': p.inShortState = false if p.cmd[i] == '\0': p.inShortState = false
p.pos = i p.pos = i
@ -91,8 +91,8 @@ proc next*(p: var TOptParser) {.
var i = p.pos var i = p.pos
while p.cmd[i] in {'\x09', ' '}: inc(i) while p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = i p.pos = i
setlen(p.key, 0) setlen(p.key.string, 0)
setlen(p.val, 0) setlen(p.val.string, 0)
if p.inShortState: if p.inShortState:
handleShortOption(p) handleShortOption(p)
return return
@ -104,29 +104,29 @@ proc next*(p: var TOptParser) {.
if p.cmd[i] == '-': if p.cmd[i] == '-':
p.kind = cmdLongOption p.kind = cmdLongOption
inc(i) inc(i)
i = parseWord(p.cmd, i, p.key, {'\0', ' ', '\x09', ':', '='}) i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='})
while p.cmd[i] in {'\x09', ' '}: inc(i) while p.cmd[i] in {'\x09', ' '}: inc(i)
if p.cmd[i] in {':', '='}: if p.cmd[i] in {':', '='}:
inc(i) inc(i)
while p.cmd[i] in {'\x09', ' '}: inc(i) while p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = parseWord(p.cmd, i, p.val) p.pos = parseWord(p.cmd, i, p.val.string)
else: else:
p.pos = i p.pos = i
else: else:
p.pos = i p.pos = i
handleShortOption(p) handleShortOption(p)
else: else:
p.kind = cmdArgument p.kind = cmdArgument
p.pos = parseWord(p.cmd, i, p.key) p.pos = parseWord(p.cmd, i, p.key.string)
proc cmdLineRest*(p: TOptParser): string {. proc cmdLineRest*(p: TOptParser): TaintedString {.
rtl, extern: "npo$1".} = rtl, extern: "npo$1".} =
## retrieves the rest of the command line that has not been parsed yet. ## retrieves the rest of the command line that has not been parsed yet.
result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)) result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
when defined(initOptParser): when defined(initOptParser):
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] = iterator getopt*(): tuple[kind: TCmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over the command line. ## This is an convenience iterator for iterating over the command line.
## This uses the TOptParser object. Example: ## This uses the TOptParser object. Example:
## ##

View file

@ -525,11 +525,11 @@ proc select*(readfds: var seq[TSocket], timeout = 500): int =
pruneSocketSet(readfds, (rd)) pruneSocketSet(readfds, (rd))
proc recvLine*(socket: TSocket, line: var string): bool = proc recvLine*(socket: TSocket, line: var TaintedString): bool =
## returns false if no further data is available. `Line` must be initialized ## returns false if no further data is available. `Line` must be initialized
## and not nil! This does not throw an EOS exception, therefore ## and not nil! This does not throw an EOS exception, therefore
## it can be used in both blocking and non-blocking sockets. ## it can be used in both blocking and non-blocking sockets.
setLen(line, 0) setLen(line.string, 0)
while true: while true:
var c: char var c: char
var n = recv(cint(socket), addr(c), 1, 0'i32) var n = recv(cint(socket), addr(c), 1, 0'i32)
@ -541,20 +541,20 @@ proc recvLine*(socket: TSocket, line: var string): bool =
elif n <= 0: return false elif n <= 0: return false
return true return true
elif c == '\L': return true elif c == '\L': return true
add(line, c) add(line.string, c)
proc recv*(socket: TSocket, data: pointer, size: int): int = proc recv*(socket: TSocket, data: pointer, size: int): int =
## receives data from a socket ## receives data from a socket
result = recv(cint(socket), data, size, 0'i32) result = recv(cint(socket), data, size, 0'i32)
proc recv*(socket: TSocket): string = proc recv*(socket: TSocket): TaintedString =
## receives all the data from the socket. ## receives all the data from the socket.
## Socket errors will result in an ``EOS`` error. ## Socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected ## If socket is not a connectionless socket and socket is not connected
## ``""`` will be returned. ## ``""`` will be returned.
const bufSize = 200 const bufSize = 1000
var buf = newString(bufSize) var buf = newString(bufSize)
result = "" result = TaintedString""
while true: while true:
var bytesRead = recv(socket, cstring(buf), bufSize-1) var bytesRead = recv(socket, cstring(buf), bufSize-1)
# Error # Error
@ -562,16 +562,16 @@ proc recv*(socket: TSocket): string =
buf[bytesRead] = '\0' # might not be necessary buf[bytesRead] = '\0' # might not be necessary
setLen(buf, bytesRead) setLen(buf, bytesRead)
add(result, buf) add(result.string, buf)
if bytesRead != bufSize-1: break if bytesRead != bufSize-1: break
proc recvAsync*(socket: TSocket, s: var string): bool = proc recvAsync*(socket: TSocket, s: var TaintedString): bool =
## receives all the data from a non-blocking socket. If socket is non-blocking ## receives all the data from a non-blocking socket. If socket is non-blocking
## and there are no messages available, `False` will be returned. ## and there are no messages available, `False` will be returned.
## Other socket errors will result in an ``EOS`` error. ## Other socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected ## If socket is not a connectionless socket and socket is not connected
## ``s`` will be set to ``""``. ## ``s`` will be set to ``""``.
const bufSize = 200 const bufSize = 1000
var buf = newString(bufSize) var buf = newString(bufSize)
s = "" s = ""
while true: while true:
@ -597,7 +597,7 @@ proc recvAsync*(socket: TSocket, s: var string): bool =
proc skip*(socket: TSocket) = proc skip*(socket: TSocket) =
## skips all the data that is pending for the socket ## skips all the data that is pending for the socket
const bufSize = 200 const bufSize = 1000
var buf = alloc(bufSize) var buf = alloc(bufSize)
while recv(socket, buf, bufSize) == bufSize: nil while recv(socket, buf, bufSize) == bufSize: nil
dealloc(buf) dealloc(buf)

View file

@ -80,24 +80,24 @@ proc readFloat64*(s: PStream): float64 =
## reads a float64 from the stream `s`. Raises `EIO` if an error occured. ## reads a float64 from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readStr*(s: PStream, length: int): string = proc readStr*(s: PStream, length: int): TaintedString =
## reads a string of length `length` from the stream `s`. Raises `EIO` if ## reads a string of length `length` from the stream `s`. Raises `EIO` if
## an error occured. ## an error occured.
result = newString(length) result = newString(length).TaintedString
var L = s.readData(s, addr(result[0]), length) var L = s.readData(s, addr(string(result)[0]), length)
if L != length: setLen(result, L) if L != length: setLen(result.string, L)
proc readLine*(s: PStream): string = proc readLine*(s: PStream): TaintedString =
## Reads a line from a stream `s`. Note: This is not very efficient. Raises ## Reads a line from a stream `s`. Note: This is not very efficient. Raises
## `EIO` if an error occured. ## `EIO` if an error occured.
result = "" result = TaintedString""
while not s.atEnd(s): while not s.atEnd(s):
var c = readChar(s) var c = readChar(s)
if c == '\c': if c == '\c':
c = readChar(s) c = readChar(s)
break break
elif c == '\L' or c == '\0': break elif c == '\L' or c == '\0': break
result.add(c) result.string.add(c)
type type
PStringStream* = ref TStringStream ## a stream that encapsulates a string PStringStream* = ref TStringStream ## a stream that encapsulates a string

View file

@ -136,7 +136,8 @@ proc RaiseFormatException(s: string) =
proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string = proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string =
if hasKey(t, key): return t[key] if hasKey(t, key): return t[key]
if useEnvironment in flags: result = os.getEnv(key) # hm difficult: assume safety in taint mode here. XXX This is dangerous!
if useEnvironment in flags: result = os.getEnv(key).string
else: result = "" else: result = ""
if result.len == 0: if result.len == 0:
if useKey in flags: result = '$' & key if useKey in flags: result = '$' & key

View file

@ -787,9 +787,9 @@ proc compileOption*(option, arg: string): bool {.
const const
hasThreadSupport = compileOption("threads") hasThreadSupport = compileOption("threads")
hasSharedHeap = defined(boehmgc) # don't share heaps; every thread has its own hasSharedHeap = defined(boehmgc) # don't share heaps; every thread has its own
# taintMode = compileOption("taintmode") taintMode = compileOption("taintmode")
when defined(taintMode): when taintMode:
# XXX use a compile time option for it! # XXX use a compile time option for it!
type TaintedString* = distinct string ## a distinct string type that type TaintedString* = distinct string ## a distinct string type that
## is `tainted`:idx:. It is an alias for ## is `tainted`:idx:. It is an alias for

View file

@ -56,8 +56,12 @@ proc rawReadLine(f: TFile, result: var string) =
add result, chr(int(c)) add result, chr(int(c))
proc readLine(f: TFile): TaintedString = proc readLine(f: TFile): TaintedString =
result = TaintedString("") when taintMode:
rawReadLine(f, result) result = TaintedString""
rawReadLine(f, result.string)
else:
result = ""
rawReadLine(f, result)
proc write(f: TFile, i: int) = proc write(f: TFile, i: int) =
when sizeof(int) == 8: when sizeof(int) == 8:
@ -86,9 +90,14 @@ proc readFile(filename: string): TaintedString =
try: try:
var len = getFileSize(f) var len = getFileSize(f)
if len < high(int): if len < high(int):
result = newString(int(len)) when taintMode:
if readBuffer(f, addr(result[0]), int(len)) != len: result = newString(int(len)).TaintedString
raiseEIO("error while reading from file") if readBuffer(f, addr(string(result)[0]), int(len)) != len:
raiseEIO("error while reading from file")
else:
result = newString(int(len))
if readBuffer(f, addr(result[0]), int(len)) != len:
raiseEIO("error while reading from file")
else: else:
raiseEIO("file too big to fit in memory") raiseEIO("file too big to fit in memory")
finally: finally:

View file

@ -1,10 +1,10 @@
# Test the optional pragma # Test the discardable pragma
proc p(x, y: int): int {.optional.} = proc p(x, y: int): int {.discardable.} =
return x + y return x + y
# test that it is inherited from generic procs too: # test that it is inherited from generic procs too:
proc q[T](x, y: T): T {.optional.} = proc q[T](x, y: T): T {.discardable.} =
return x + y return x + y

View file

@ -1,14 +1,13 @@
Version 0.8.14 Version 0.8.14
============== ==============
- 'let x = y' - taint mode
- 'let x = y'; const ptr/ref
- fix actors.nim - fix actors.nim
- make threadvar efficient again on linux after testing - make threadvar efficient again on linux after testing
- fix the 'const' issues - fix the 'const' issues
- test the sort implementation again - test the sort implementation again
- optional indentation for 'case' statement - optional indentation for 'case' statement
- taint mode
- const ptr/ref
version 0.9.0 version 0.9.0

View file

@ -126,22 +126,22 @@ proc parseCmdLine(c: var TConfigData) =
next(p) next(p)
var kind = p.kind var kind = p.kind
var key = p.key var key = p.key
var val = p.val var val = p.val.string
case kind case kind
of cmdArgument: of cmdArgument:
if c.actions == {}: if c.actions == {}:
for a in split(normalize(key), {';', ','}): for a in split(normalize(key.string), {';', ','}):
case a case a
of "csource": incl(c.actions, actionCSource) of "csource": incl(c.actions, actionCSource)
of "zip": incl(c.actions, actionZip) of "zip": incl(c.actions, actionZip)
of "inno": incl(c.actions, actionInno) of "inno": incl(c.actions, actionInno)
else: quit(Usage) else: quit(Usage)
else: else:
c.infile = addFileExt(key, "ini") c.infile = addFileExt(key.string, "ini")
c.nimrodArgs = cmdLineRest(p) c.nimrodArgs = cmdLineRest(p).string
break break
of cmdLongOption, cmdShortOption: of cmdLongOption, cmdShortOption:
case normalize(key) case normalize(key.string)
of "help", "h": of "help", "h":
stdout.write(Usage) stdout.write(Usage)
quit(0) quit(0)

View file

@ -47,8 +47,8 @@ Language Additions
- Return types may be of the type ``var T`` to return an l-value. - Return types may be of the type ``var T`` to return an l-value.
- The error pragma can now be used to mark symbols whose *usage* should trigger - The error pragma can now be used to mark symbols whose *usage* should trigger
a compile-time error. a compile-time error.
- There is a new ``optional`` pragma that can be used to mark a routine so that - There is a new ``discardable`` pragma that can be used to mark a routine
its result can be discarded implicitely. so that its result can be discarded implicitely.
Compiler Additions Compiler Additions