init checks and 'out' parameters (#14521)

* I don't care about observable stores
* enforce explicit initializations
* cleaner code for the stdlib
* stdlib: use explicit initializations
* make tests green
* algorithm.nim: set result explicitly
* remove out parameters and bring the PR into a mergable state
* updated the changelog
This commit is contained in:
Andreas Rumpf 2020-06-23 10:53:57 +02:00 • committed by GitHub
commit da29222f86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 355 additions and 257 deletions

View file

@ -187,6 +187,7 @@ proc binarySearch*[T, K](a: openArray[T], key: K,
else:
return -1
result = 0
if (len and (len - 1)) == 0:
# when `len` is a power of 2, a faster shr can be used.
var step = len shr 1

View file

@ -395,6 +395,8 @@ proc firstSetBitNim(x: uint64): int {.inline, noSideEffect.} =
if k == 0:
k = uint32(v shr 32'u32) and 0xFFFFFFFF'u32
result = 32
else:
result = 0
result += firstSetBitNim(k)
proc fastlog2Nim(x: uint32): int {.inline, noSideEffect.} =

View file

@ -417,7 +417,8 @@ proc relativePath*(path, base: string, sep = DirSep): string {.
if not sameRoot(path, base):
return path
var f, b: PathIter
var f = default PathIter
var b = default PathIter
var ff = (0, -1)
var bb = (0, -1) # (int, int)
result = newStringOfCap(path.len)
@ -1053,6 +1054,7 @@ when defined(windows) or defined(posix) or defined(nintendoswitch):
assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\""
# can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
result = ""
for i in 0..<args.len:
if i > 0: result.add " "
result.add quoteShell(args[i])
@ -2981,7 +2983,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW
setLen(result, L)
break
elif defined(macosx):
var size: cuint32
var size = cuint32(0)
getExecPath1(nil, size)
result = newString(int(size))
if getExecPath2(result, size):

View file

@ -249,6 +249,7 @@ proc skipWhitespace*(s: string, start = 0): int {.inline.} =
doAssert skipWhitespace(" Hello World", 0) == 1
doAssert skipWhitespace("Hello World", 5) == 1
doAssert skipWhitespace("Hello World", 5) == 2
result = 0
while start+result < s.len and s[start+result] in Whitespace: inc(result)
proc skip*(s, token: string, start = 0): int {.inline.} =
@ -260,6 +261,7 @@ proc skip*(s, token: string, start = 0): int {.inline.} =
doAssert skip("2019-01-22", "19", 2) == 2
doAssert skip("CAPlow", "CAP", 0) == 3
doAssert skip("CAPlow", "cap", 0) == 0
result = 0
while start+result < s.len and result < token.len and
s[result+start] == token[result]:
inc(result)
@ -270,6 +272,7 @@ proc skipIgnoreCase*(s, token: string, start = 0): int =
runnableExamples:
doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
result = 0
while start+result < s.len and result < token.len and
toLower(s[result+start]) == toLower(token[result]): inc(result)
if result != token.len: result = 0
@ -282,6 +285,7 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
doAssert skipUntil("Hello World", {'W'}, 0) == 6
doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
result = 0
while start+result < s.len and s[result+start] notin until: inc(result)
proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
@ -293,6 +297,7 @@ proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
doAssert skipUntil("Hello World", 'o', 4) == 0
doAssert skipUntil("Hello World", 'W', 0) == 6
doAssert skipUntil("Hello World", 'w', 0) == 11
result = 0
while start+result < s.len and s[result+start] != until: inc(result)
proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
@ -302,6 +307,7 @@ proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
doAssert skipWhile("Hello World", {'H', 'e'}) == 2
doAssert skipWhile("Hello World", {'e'}) == 0
doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
result = 0
while start+result < s.len and s[result+start] in toSkip: inc(result)
proc parseUntil*(s: string, token: var string, until: set[char],
@ -437,7 +443,7 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
var res: BiggestInt
doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
doAssert res == 9223372036854775807
var res: BiggestInt
var res = BiggestInt(0)
# use 'res' for exception safety (don't write to 'number' in case of an
# overflow exception):
result = rawParseInt(s, res, start)
@ -455,7 +461,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
doAssert res == 2019
doAssert parseInt("2019", res, 2) == 2
doAssert res == 19
var res: BiggestInt
var res = BiggestInt(0)
result = parseBiggestInt(s, res, start)
when sizeof(int) <= 4:
if res < low(int) or res > high(int):
@ -518,7 +524,7 @@ proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
doAssert res == 12
doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19
doAssert res == 1111111111111111111'u64
var res: BiggestUInt
var res = BiggestUInt(0)
# use 'res' for exception safety (don't write to 'number' in case of an
# overflow exception):
result = rawParseUInt(s, res, start)
@ -536,7 +542,7 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {.
doAssert res == 3450
doAssert parseUInt("3450", res, 2) == 2
doAssert res == 50
var res: BiggestUInt
var res = BiggestUInt(0)
result = parseBiggestUInt(s, res, start)
when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
if res > 0xFFFF_FFFF'u64:
@ -563,7 +569,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
doAssert res == 32.57
doAssert parseFloat("32.57", res, 3) == 2
doAssert res == 57.00
var bf: BiggestFloat
var bf = BiggestFloat(0.0)
result = parseBiggestFloat(s, bf, start)
if result != 0:
number = bf

View file

@ -44,7 +44,7 @@ proc next*(it: var PathIter; x: string): (int, int) =
it.notFirst = true
iterator dirs(x: string): (int, int) =
var it: PathIter
var it = default PathIter
while hasNext(it, x): yield next(it, x)
proc isDot(x: string; bounds: (int, int)): bool =

View file

@ -1103,6 +1103,7 @@ proc parseInt*(s: string): int {.noSideEffect,
## If `s` is not a valid integer, `ValueError` is raised.
runnableExamples:
doAssert parseInt("-0042") == -42
result = 0
let L = parseutils.parseInt(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s)
@ -1112,6 +1113,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect,
## Parses a decimal integer value contained in `s`.
##
## If `s` is not a valid integer, `ValueError` is raised.
result = BiggestInt(0)
let L = parseutils.parseBiggestInt(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s)
@ -1121,6 +1123,7 @@ proc parseUInt*(s: string): uint {.noSideEffect,
## Parses a decimal unsigned integer value contained in `s`.
##
## If `s` is not a valid integer, `ValueError` is raised.
result = uint(0)
let L = parseutils.parseUInt(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid unsigned integer: " & s)
@ -1130,6 +1133,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect,
## Parses a decimal unsigned integer value contained in `s`.
##
## If `s` is not a valid integer, `ValueError` is raised.
result = BiggestUInt(0)
let L = parseutils.parseBiggestUInt(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid unsigned integer: " & s)
@ -1143,6 +1147,7 @@ proc parseFloat*(s: string): float {.noSideEffect,
runnableExamples:
doAssert parseFloat("3.14") == 3.14
doAssert parseFloat("inf") == 1.0/0
result = 0.0
let L = parseutils.parseFloat(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid float: " & s)
@ -1161,6 +1166,7 @@ proc parseBinInt*(s: string): int {.noSideEffect,
doAssert a.parseBinInt() == 53
doAssert b.parseBinInt() == 7
result = 0
let L = parseutils.parseBin(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid binary integer: " & s)
@ -1172,6 +1178,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one
## of the following optional prefixes: ``0o``, ``0O``. Underscores within
## `s` are ignored.
result = 0
let L = parseutils.parseOct(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid oct integer: " & s)
@ -1183,6 +1190,7 @@ proc parseHexInt*(s: string): int {.noSideEffect,
## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
## within `s` are ignored.
result = 0
let L = parseutils.parseHex(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid hex integer: " & s)
@ -1270,9 +1278,9 @@ macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped =
result = nnkCaseStmt.newTree(nnkDotExpr.newTree(argSym,
bindSym"nimIdentNormalize"))
# stores all processed field strings to give error msg for ambiguous enums
var foundFields: seq[string]
var fStr: string # string of current field
var fNum: BiggestInt # int value of current field
var foundFields: seq[string] = @[]
var fStr = "" # string of current field
var fNum = BiggestInt(0) # int value of current field
for f in impl:
case f.kind
of nnkEmpty: continue # skip first node of `enumTy`
@ -2028,6 +2036,7 @@ proc rfind*(s, sub: string, start: Natural = 0, last = -1): int {.noSideEffect,
if sub.len == 0:
return -1
let last = if last == -1: s.high else: last
result = 0
for i in countdown(last - sub.len + 1, start):
for j in 0..sub.len-1:
result = i
@ -2044,6 +2053,7 @@ proc count*(s: string, sub: char): int {.noSideEffect,
##
## See also:
## * `countLines proc<#countLines,string>`_
result = 0
for c in s:
if c == sub: inc result
@ -2054,6 +2064,7 @@ proc count*(s: string, subs: set[char]): int {.noSideEffect,
## See also:
## * `countLines proc<#countLines,string>`_
doAssert card(subs) > 0
result = 0
for c in s:
if c in subs: inc result
@ -2066,6 +2077,7 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
## See also:
## * `countLines proc<#countLines,string>`_
doAssert sub.len > 0
result = 0
var i = 0
while true:
i = s.find(sub, i)
@ -2313,7 +2325,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
case s[i+1]:
of 'x':
inc i, 2
var c: int
var c = 0
i += parseutils.parseHex(s, c, i, maxLen = 2)
result.add(chr(c))
dec i, 2
@ -2514,7 +2526,7 @@ proc formatSize*(bytes: int64,
xb: int64 = bytes
fbytes: float
lastXb: int64 = bytes
matchedIndex: int
matchedIndex = 0
prefixes: array[9, string]
if prefix == bpColloquial:
prefixes = collPrefixes

View file

@ -895,16 +895,16 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
millis mod convert(Seconds, Milliseconds, 1).int)
result = initTime(seconds, nanos)
elif defined(macosx):
var a: Timeval
var a {.noinit.}: Timeval
gettimeofday(a)
result = initTime(a.tv_sec.int64,
convert(Microseconds, Nanoseconds, a.tv_usec.int))
elif defined(posix):
var ts: Timespec
var ts {.noinit.}: Timespec
discard clock_gettime(CLOCK_REALTIME, ts)
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
elif defined(windows):
var f: FILETIME
var f {.noinit.}: FILETIME
getSystemTimeAsFileTime(f)
result = fromWinTime(rdFileTime(f))
@ -1756,7 +1756,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string,
proc parsePattern(input: string, pattern: FormatPattern, i: var int,
parsed: var ParsedTime, loc: DateTimeLocale): bool =
template takeInt(allowedWidth: Slice[int], allowSign = false): int =
var sv: int
var sv = 0
var pd = parseInt(input, sv, i, allowedWidth.b, allowSign)
if pd < allowedWidth.a:
return false
@ -1987,6 +1987,7 @@ proc format*(dt: DateTime, f: TimeFormat,
let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc())
doAssert "2000-01-01" == dt.format(f)
assertDateTimeInitialized dt
result = ""
var idx = 0
while idx <= f.patterns.high:
case f.patterns[idx].FormatPattern
@ -2284,7 +2285,7 @@ proc between*(startDt, endDt: DateTime): TimeInterval =
endDate.monthday.dec
# Years
result.years.inc endDate.year - startDate.year - 1
result.years = endDate.year - startDate.year - 1
if (startDate.month, startDate.monthday) <= (endDate.month, endDate.monthday):
result.years.inc
startDate.year.inc result.years
@ -2441,6 +2442,7 @@ proc evaluateInterval(dt: DateTime, interval: TimeInterval):
var months = interval.years * 12 + interval.months
var curYear = dt.year
var curMonth = dt.month
result = default(tuple[adjDur, absDur: Duration])
# Subtracting
if months < 0:
for mth in countdown(-1 * months, 1):
@ -2562,17 +2564,17 @@ proc epochTime*(): float {.tags: [TimeEffect].} =
##
## ``getTime`` should generally be preferred over this proc.
when defined(macosx):
var a: Timeval
var a {.noinit.}: Timeval
gettimeofday(a)
result = toBiggestFloat(a.tv_sec.int64) + toBiggestFloat(
a.tv_usec)*0.00_0001
elif defined(posix):
var ts: Timespec
var ts {.noinit.}: Timespec
discard clock_gettime(CLOCK_REALTIME, ts)
result = toBiggestFloat(ts.tv_sec.int64) +
toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000
elif defined(windows):
var f: winlean.FILETIME
var f {.noinit.}: winlean.FILETIME
getSystemTimeAsFileTime(f)
var i64 = rdFileTime(f) - epochDiff
var secs = i64 div rateDiff

View file

@ -112,7 +112,7 @@ macro genericParamsImpl(T: typedesc): untyped =
of nnkBracketExpr:
for i in 1..<impl.len:
let ai = impl[i]
var ret: NimNode
var ret: NimNode = nil
case ai.typeKind
of ntyTypeDesc:
ret = ai

View file

@ -39,6 +39,7 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
doAssert a.runeLen == 6
## note: a.len == 8
result = 0
var i = 0
while i < len(s):
if uint(s[i]) <= 127: inc(i)