merge devel

This commit is contained in:
cooldome 2018-04-29 23:28:18 +01:00
commit 3e5c94664d
25 changed files with 408 additions and 243 deletions

View file

@ -11,7 +11,7 @@
to deal with! to deal with!
- Indexing into a ``cstring`` for the JS target is now mapped - Indexing into a ``cstring`` for the JS target is now mapped
to ``charCodeAt``. to ``charCodeAt``.
- Assignments that would "slice" an object into its supertype are not prevented - Assignments that would "slice" an object into its supertype are now prevented
at runtime. Use ``ref object`` with inheritance rather than ``object`` with at runtime. Use ``ref object`` with inheritance rather than ``object`` with
inheritance to prevent this issue. inheritance to prevent this issue.
@ -81,7 +81,10 @@
Imported exceptions can be raised and caught just like Nim exceptions. Imported exceptions can be raised and caught just like Nim exceptions.
More details in language manual. More details in language manual.
- Range float typed, example `range[0.0 .. Inf]`. More details in language manual. - ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``.
- Range float types, example `range[0.0 .. Inf]`. More details in language manual.
### Tool changes ### Tool changes

View file

@ -125,15 +125,15 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
of tyString, tySequence: of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar and if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module): not compileToCpp(p.module):
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
else: else:
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)] result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
of tyArray: of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))] result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
of tyPtr, tyRef: of tyPtr, tyRef:
case lastSon(a.t).kind case lastSon(a.t).kind
of tyString, tySequence: of tyString, tySequence:
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
of tyArray: of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))] result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
else: else:
@ -143,7 +143,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} = proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
result = "$1->data" % [a.rdLoc] result = "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc]
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
var a: TLoc var a: TLoc

View file

@ -882,7 +882,7 @@ proc genIndexCheck(p: BProc; arr, idx: TLoc) =
rdCharLoc(idx), first, intLiteral(lastOrd(ty))) rdCharLoc(idx), first, intLiteral(lastOrd(ty)))
of tySequence, tyString: of tySequence, tyString:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(idx), rdLoc(arr), lenField(p)) rdLoc(idx), rdLoc(arr), lenField(p))
else: discard else: discard
@ -907,11 +907,11 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
if ty.kind == tyString: if ty.kind == tyString:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a), lenField(p)) rdLoc(b), rdLoc(a), lenField(p))
else: else:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a), lenField(p)) rdLoc(b), rdLoc(a), lenField(p))
if d.k == locNone: d.storage = OnHeap if d.k == locNone: d.storage = OnHeap
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
@ -980,10 +980,10 @@ proc genEcho(p: BProc, n: PNode) =
var a: TLoc var a: TLoc
for it in n.sons: for it in n.sons:
if it.skipConv.kind == nkNilLit: if it.skipConv.kind == nkNilLit:
add(args, ", \"nil\"") add(args, ", \"\"")
else: else:
initLocExpr(p, it, a) initLocExpr(p, it, a)
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) addf(args, ", $1? ($1)->data:\"\"", [rdLoc(a)])
p.module.includeHeader("<base/log.h>") p.module.includeHeader("<base/log.h>")
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
else: else:
@ -1034,7 +1034,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
inc(L, len(e.sons[i + 1].strVal)) inc(L, len(e.sons[i + 1].strVal))
else: else:
addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L)) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
add(p.s(cpsStmts), appends) add(p.s(cpsStmts), appends)
@ -1073,7 +1073,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
inc(L, len(e.sons[i + 2].strVal)) inc(L, len(e.sons[i + 2].strVal))
else: else:
addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
add(appends, rfmt(p.module, "#appendString($1, $2);$n", add(appends, rfmt(p.module, "#appendString($1, $2);$n",
rdLoc(dest), rdLoc(a))) rdLoc(dest), rdLoc(a)))
linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
@ -1086,17 +1086,17 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
# seq->data[seq->len-1] = x; # seq->data[seq->len-1] = x;
let seqAppendPattern = if not p.module.compileToCpp: let seqAppendPattern = if not p.module.compileToCpp:
"$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n" "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
else: else:
"$1 = ($2) #incrSeqV2($1, sizeof($3));$n" "$1 = ($2) #incrSeqV3($1, $3);$n"
var a, b, dest, tmpL: TLoc var a, b, dest, tmpL: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
let bt = skipTypes(e.sons[2].typ, {tyVar}) let seqType = skipTypes(e.sons[1].typ, {tyVar})
lineCg(p, cpsStmts, seqAppendPattern, [ lineCg(p, cpsStmts, seqAppendPattern, [
rdLoc(a), rdLoc(a),
getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.sons[1].typ),
getTypeDesc(p.module, bt)]) genTypeInfo(p.module, seqType, e.info)])
#if bt != b.t: #if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
initLoc(dest, locExpr, e.sons[2], OnHeap) initLoc(dest, locExpr, e.sons[2], OnHeap)
@ -1417,7 +1417,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
of tyString, tySequence: of tyString, tySequence:
putIntoDest(p, b, e, putIntoDest(p, b, e,
"$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage) "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage)
of tyArray: of tyArray:
putIntoDest(p, b, e, putIntoDest(p, b, e,
"$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage) "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
@ -1500,13 +1500,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
let t = skipTypes(e.sons[1].typ, {tyVar}) let t = skipTypes(e.sons[1].typ, {tyVar})
let setLenPattern = if not p.module.compileToCpp: let setLenPattern = if not p.module.compileToCpp:
"$1 = ($3) #setLengthSeq(&($1)->Sup, sizeof($4), $2);$n" "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n"
else: else:
"$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n" "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
lineCg(p, cpsStmts, setLenPattern, [ lineCg(p, cpsStmts, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
getTypeDesc(p.module, t.skipTypes(abstractInst).sons[0])]) genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
gcUsage(e) gcUsage(e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
@ -1740,7 +1740,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, n, "$1->data" % [rdLoc(a)], putIntoDest(p, d, n, "($1 ? $1->data : (NCSTRING)\"\")" % [rdLoc(a)],
a.storage) a.storage)
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
@ -1755,16 +1755,14 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
var x: TLoc var x: TLoc
var a = e.sons[1] var a = e.sons[1]
var b = e.sons[2] var b = e.sons[2]
if (a.kind == nkNilLit) or (b.kind == nkNilLit): if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
binaryExpr(p, e, d, "($1 == $2)")
elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
initLocExpr(p, e.sons[2], x) initLocExpr(p, e.sons[2], x)
putIntoDest(p, d, e, putIntoDest(p, d, e,
rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""): elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
initLocExpr(p, e.sons[1], x) initLocExpr(p, e.sons[1], x)
putIntoDest(p, d, e, putIntoDest(p, d, e,
rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
else: else:
binaryExpr(p, e, d, "#eqStrings($1, $2)") binaryExpr(p, e, d, "#eqStrings($1, $2)")

View file

@ -82,7 +82,6 @@ proc countDefinedSymbols*(): int =
proc initDefines*() = proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive) gSymbols = newStringTable(modeStyleInsensitive)
defineSymbol("nimrod") # 'nimrod' is always defined
# for bootstrapping purposes and old code: # for bootstrapping purposes and old code:
defineSymbol("nimhygiene") defineSymbol("nimhygiene")
defineSymbol("niminheritable") defineSymbol("niminheritable")
@ -115,3 +114,4 @@ proc initDefines*() =
defineSymbol("nimHasNilChecks") defineSymbol("nimHasNilChecks")
defineSymbol("nimSymKind") defineSymbol("nimSymKind")
defineSymbol("nimVmEqIdent") defineSymbol("nimVmEqIdent")
defineSymbol("nimNoNil")

View file

@ -47,7 +47,7 @@ proc mainCommand =
compileProject(newModuleGraph(), newIdentCache()) compileProject(newModuleGraph(), newIdentCache())
pretty.overwriteFiles() pretty.overwriteFiles()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) = proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
var p = parseopt.initOptParser(cmd) var p = parseopt.initOptParser(cmd)
var argsCount = 0 var argsCount = 0
gOnlyMainfile = true gOnlyMainfile = true
@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
of "wholeproject": gOnlyMainfile = false of "wholeproject": gOnlyMainfile = false
of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
else: else:
processSwitch(pass, p) processSwitch(pass, p, config)
of cmdArgument: of cmdArgument:
options.gProjectName = unixToNativePath(p.key) options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break # if processArgument(pass, p, argsCount): break
proc handleCmdLine() = proc handleCmdLine(config: ConfigRef) =
if paramCount() == 0: if paramCount() == 0:
stdout.writeLine(Usage) stdout.writeLine(Usage)
else: else:
processCmdLine(passCmd1, "") processCmdLine(passCmd1, "", config)
if gProjectName != "": if gProjectName != "":
try: try:
gProjectFull = canonicalizePath(gProjectName) gProjectFull = canonicalizePath(gProjectName)
@ -96,11 +96,11 @@ proc handleCmdLine() =
gProjectName = p.name gProjectName = p.name
else: else:
gProjectPath = getCurrentDir() gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files loadConfigs(DefaultConfig, config) # load all config files
# now process command line arguments again, because some options in the # now process command line arguments again, because some options in the
# command line can overwite the config file's settings # command line can overwite the config file's settings
extccomp.initVars() extccomp.initVars()
processCmdLine(passCmd2, "") processCmdLine(passCmd2, "", config)
mainCommand() mainCommand()
when compileOption("gc", "v2") or compileOption("gc", "refc"): when compileOption("gc", "v2") or compileOption("gc", "refc"):
@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
condsyms.initDefines() condsyms.initDefines()
defineSymbol "nimfix" defineSymbol "nimfix"
handleCmdline() handleCmdline newConfigRef()

View file

@ -153,8 +153,9 @@ proc commonType*(x, y: PType): PType =
if a.kind in {tyRef, tyPtr}: if a.kind in {tyRef, tyPtr}:
k = a.kind k = a.kind
if b.kind != a.kind: return x if b.kind != a.kind: return x
a = a.lastSon # bug #7601, array construction of ptr generic
b = b.lastSon a = a.lastSon.skipTypes({tyGenericInst})
b = b.lastSon.skipTypes({tyGenericInst})
if a.kind == tyObject and b.kind == tyObject: if a.kind == tyObject and b.kind == tyObject:
result = commonSuperclass(a, b) result = commonSuperclass(a, b)
# this will trigger an error later: # this will trigger an error later:

View file

@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
filter: TSymKinds, filter: TSymKinds,
best, alt: var TCandidate, best, alt: var TCandidate,
errors: var CandidateErrors, errors: var CandidateErrors,
diagnosticsFlag = false) = diagnosticsFlag: bool,
errorsEnabled: bool) =
var o: TOverloadIter var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol) var sym = initOverloadIter(o, c, headSymbol)
var scope = o.lastOverloadScope var scope = o.lastOverloadScope
@ -68,6 +69,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# This can occur in cases like 'init(a, 1, (var b = new(Type2); b))' # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
let counterInitial = c.currentScope.symbols.counter let counterInitial = c.currentScope.symbols.counter
var syms: seq[tuple[s: PSym, scope: int]] var syms: seq[tuple[s: PSym, scope: int]]
var noSyms = true
var nextSymIndex = 0 var nextSymIndex = 0
while sym != nil: while sym != nil:
if sym.kind in filter: if sym.kind in filter:
@ -102,7 +104,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var cmp = cmpCandidates(best, z) var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far elif cmp == 0: alt = z # x is as good as the best so far
elif errors != nil or z.diagnostics != nil: elif errorsEnabled or z.diagnosticsEnabled:
errors.safeAdd(CandidateError( errors.safeAdd(CandidateError(
sym: sym, sym: sym,
unmatchedVarParam: int z.mutabilityProblem, unmatchedVarParam: int z.mutabilityProblem,
@ -113,7 +115,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# before any further candidate init and compare. SLOW, but rare case. # before any further candidate init and compare. SLOW, but rare case.
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
best, alt, o, diagnosticsFlag) best, alt, o, diagnosticsFlag)
if syms == nil: noSyms = false
if noSyms:
sym = nextOverloadIter(o, c, headSymbol) sym = nextOverloadIter(o, c, headSymbol)
scope = o.lastOverloadScope scope = o.lastOverloadScope
elif nextSymIndex < syms.len: elif nextSymIndex < syms.len:
@ -206,7 +209,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
if errorOutputs == {}: if errorOutputs == {}:
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
if errors.isNil or errors.len == 0: if errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree) localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return return
@ -227,7 +230,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
if symx.kind in routineKinds: if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx, errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0, firstMismatch: 0, unmatchedVarParam: 0, firstMismatch: 0,
diagnostics: nil)) diagnostics: nil,
enabled: false))
symx = nextOverloadIter(o, c, headSymbol) symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0: if errors.len == 0:
localError(n.info, "could not resolve: " & $n) localError(n.info, "could not resolve: " & $n)
@ -236,7 +240,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
proc resolveOverloads(c: PContext, n, orig: PNode, proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds, flags: TExprFlags, filter: TSymKinds, flags: TExprFlags,
errors: var CandidateErrors): TCandidate = errors: var CandidateErrors,
errorsEnabled: bool): TCandidate =
var initialBinding: PNode var initialBinding: PNode
var alt: TCandidate var alt: TCandidate
var f = n.sons[0] var f = n.sons[0]
@ -249,7 +254,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
template pickBest(headSymbol) = template pickBest(headSymbol) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding, pickBestCandidate(c, headSymbol, n, orig, initialBinding,
filter, result, alt, errors, efExplain in flags) filter, result, alt, errors, efExplain in flags,
errorsEnabled)
pickBest(f) pickBest(f)
let overloadsState = result.state let overloadsState = result.state
@ -423,9 +429,8 @@ proc tryDeref(n: PNode): PNode =
proc semOverloadedCall(c: PContext, n, nOrig: PNode, proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags): PNode = filter: TSymKinds, flags: TExprFlags): PNode =
var errors: CandidateErrors = if efExplain in flags: @[] var errors: CandidateErrors = if efExplain in flags: @[] else: nil
else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
if r.state == csMatch: if r.state == csMatch:
# this may be triggered, when the explain pragma is used # this may be triggered, when the explain pragma is used
if errors.len > 0: if errors.len > 0:
@ -443,7 +448,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
# into sigmatch with hidden conversion produced there # into sigmatch with hidden conversion produced there
# #
n.sons[1] = n.sons[1].tryDeref n.sons[1] = n.sons[1].tryDeref
var r = resolveOverloads(c, n, nOrig, filter, flags, errors) var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch: result = semResolvedCall(c, n, r) if r.state == csMatch: result = semResolvedCall(c, n, r)
else: else:
# get rid of the deref again for a better error message: # get rid of the deref again for a better error message:

View file

@ -63,7 +63,7 @@ type
# to the user. # to the user.
efWantStmt, efAllowStmt, efDetermineType, efExplain, efWantStmt, efAllowStmt, efDetermineType, efExplain,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck, efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo, efNoEvaluateGeneric, efInCall, efFromHlo
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]

View file

@ -51,7 +51,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
result.typ = errorType(c) result.typ = errorType(c)
else: else:
if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
@ -63,8 +62,6 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
localError(n.info, errExprXHasNoType, localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
result.typ = errorType(c) result.typ = errorType(c)
else:
semProcvarCheck(c, result)
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
@ -308,7 +305,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
maybeLiftType(t2, c, n.info) maybeLiftType(t2, c, n.info)
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) initCandidate(c, m, t2)
if efExplain in flags: m.diagnostics = @[] if efExplain in flags:
m.diagnostics = @[]
m.diagnosticsEnabled = true
let match = typeRel(m, t2, t1) >= isSubtype # isNone let match = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(match)) result = newIntNode(nkIntLit, ord(match))
@ -540,7 +539,6 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# we need to recurse explicitly here as converters can create nested # we need to recurse explicitly here as converters can create nested
# calls and then they wouldn't be analysed otherwise # calls and then they wouldn't be analysed otherwise
analyseIfAddressTakenInCall(c, n.sons[i]) analyseIfAddressTakenInCall(c, n.sons[i])
semProcvarCheck(c, n.sons[i])
if i < sonsLen(t) and if i < sonsLen(t) and
skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
if n.sons[i].kind != nkHiddenAddr: if n.sons[i].kind != nkHiddenAddr:
@ -1245,7 +1243,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
# make sure we don't evaluate generic macros/templates # make sure we don't evaluate generic macros/templates
n.sons[0] = semExprWithType(c, n.sons[0], n.sons[0] = semExprWithType(c, n.sons[0],
{efNoProcvarCheck, efNoEvaluateGeneric}) {efNoEvaluateGeneric})
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
case arr.kind case arr.kind

View file

@ -71,37 +71,12 @@ proc toCover(t: PType): BiggestInt =
else: else:
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
when false:
proc performProcvarCheck(c: PContext, info: TLineInfo, s: PSym) =
## Checks that the given symbol is a proper procedure variable, meaning
## that it
var smoduleId = getModule(s).id
if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
smoduleId != c.module.id:
block outer:
for module in c.friendModules:
if smoduleId == module.id:
break outer
localError(info, errXCannotBePassedToProcVar, s.name.s)
template semProcvarCheck(c: PContext, n: PNode) =
when false:
var n = n.skipConv
if n.kind in nkSymChoices:
for x in n:
if x.sym.kind in {skProc, skMethod, skConverter, skIterator}:
performProcvarCheck(c, n.info, x.sym)
elif n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
skIterator}:
performProcvarCheck(c, n.info, n.sym)
proc semProc(c: PContext, n: PNode): PNode proc semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode): PNode = proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n) result = semExpr(c, n)
if result.typ != nil: if result.typ != nil:
# XXX tyGenericInst here? # XXX tyGenericInst here?
semProcvarCheck(c, result)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprBranchScope(c: PContext, n: PNode): PNode = proc semExprBranchScope(c: PContext, n: PNode): PNode =
@ -384,7 +359,7 @@ proc checkNilable(v: PSym) =
include semasgn include semasgn
proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
let L = identDefs.len let L = identDefs.len
let value = identDefs[L-1] let value = identDefs[L-1]
if result.kind == nkStmtList: if result.kind == nkStmtList:
@ -525,7 +500,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
localError(a.info, errXExpected, "tuple") localError(a.info, errXExpected, "tuple")
elif length-2 != sonsLen(tup): elif length-2 != sonsLen(tup):
localError(a.info, errWrongNumberOfVariables) localError(a.info, errWrongNumberOfVariables)
else:
b = newNodeI(nkVarTuple, a.info) b = newNodeI(nkVarTuple, a.info)
newSons(b, length) newSons(b, length)
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
@ -572,7 +546,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addToVarSection(c, result, n, b) addToVarSection(c, result, n, b)
else: else:
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
setVarType(v, tup.sons[j]) # bug #7663, for 'nim check' this can be a non-tuple:
if tup.kind == tyTuple: setVarType(v, tup.sons[j])
else: v.typ = tup
b.sons[j] = newSymNode(v) b.sons[j] = newSymNode(v)
checkNilable(v) checkNilable(v)
if sfCompileTime in v.flags: hasCompileTime = true if sfCompileTime in v.flags: hasCompileTime = true
@ -1588,9 +1564,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}: if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s) localError(n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"dotOperators\".}")
elif s.name.s == "()" and callOperator notin c.features: elif s.name.s == "()" and callOperator notin c.features:
message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s) localError(n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"callOperator\".}")
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc! # for DLL generation it is annoying to check for sfImportc!
@ -1682,11 +1660,6 @@ proc semIterator(c: PContext, n: PNode): PNode =
incl(s.typ.flags, tfCapturesEnv) incl(s.typ.flags, tfCapturesEnv)
else: else:
s.typ.callConv = ccInline s.typ.callConv = ccInline
when false:
if s.typ.callConv != ccInline:
s.typ.callConv = ccClosure
# and they always at least use the 'env' for the state field:
incl(s.typ.flags, tfCapturesEnv)
if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone: if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
localError(n.info, errImplOfXexpected, s.name.s) localError(n.info, errImplOfXexpected, s.name.s)
@ -1792,14 +1765,6 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
evalStaticStmt(c.module, c.cache, a, c.p.owner) evalStaticStmt(c.module, c.cache, a, c.p.owner)
result = newNodeI(nkDiscardStmt, n.info, 1) result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode result.sons[0] = emptyNode
when false:
result = evalStaticStmt(c.module, a, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode
elif result.kind == nkEmpty:
result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode
proc usesResult(n: PNode): bool = proc usesResult(n: PNode): bool =
# nkStmtList(expr) properly propagates the void context, # nkStmtList(expr) properly propagates the void context,
@ -1841,40 +1806,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# nkNilLit, nkEmpty}: # nkNilLit, nkEmpty}:
# dec last # dec last
for i in countup(0, length - 1): for i in countup(0, length - 1):
let k = n.sons[i].kind
case k
of nkFinally, nkExceptBranch:
# stand-alone finally and except blocks are
# transformed into regular try blocks:
#
# var f = fopen("somefile") | var f = fopen("somefile")
# finally: fclose(f) | try:
# ... | ...
# | finally:
# | fclose(f)
var deferPart: PNode
if k == nkDefer:
deferPart = newNodeI(nkFinally, n.sons[i].info)
deferPart.add n.sons[i].sons[0]
elif k == nkFinally:
message(n.info, warnDeprecated,
"use 'defer'; standalone 'finally'")
deferPart = n.sons[i]
else:
message(n.info, warnDeprecated,
"use an explicit 'try'; standalone 'except'")
deferPart = n.sons[i]
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
var body = newNodeI(nkStmtList, n.sons[i].info)
if i < n.sonsLen - 1:
body.sons = n.sons[(i+1)..n.len-1]
tryStmt.addSon(body)
tryStmt.addSon(deferPart)
n.sons[i] = semTry(c, tryStmt)
n.sons.setLen(i+1)
n.typ = n.sons[i].typ
return
else:
var expr = semExpr(c, n.sons[i], flags) var expr = semExpr(c, n.sons[i], flags)
n.sons[i] = expr n.sons[i] = expr
if c.matchedConcept != nil and expr.typ != nil and if c.matchedConcept != nil and expr.typ != nil and
@ -1908,7 +1839,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr if not isEmptyType(n.typ): n.kind = nkStmtListExpr
if n.sons[i].kind in LastBlockStmts or if n.sons[i].kind in LastBlockStmts or
n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags: n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and
sfNoReturn in n.sons[i][0].sym.flags:
for j in countup(i + 1, length - 1): for j in countup(i + 1, length - 1):
case n.sons[j].kind case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
@ -1930,15 +1862,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
not (result.comment[0] == '#' and result.comment[1] == '#'): not (result.comment[0] == '#' and result.comment[1] == '#'):
# it is an old-style comment statement: we replace it with 'discard ""': # it is an old-style comment statement: we replace it with 'discard ""':
prettybase.replaceComment(result.info) prettybase.replaceComment(result.info)
when false:
# a statement list (s; e) has the type 'e':
if result.kind == nkStmtList and result.len > 0:
var lastStmt = lastSon(result)
if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt):
result.typ = lastStmt.typ
#localError(lastStmt.info, errGenerated,
# "Last expression must be explicitly returned if it " &
# "is discardable or discarded")
proc semStmt(c: PContext, n: PNode): PNode = proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias: # now: simply an alias:

View file

@ -26,6 +26,7 @@ type
sym*: PSym sym*: PSym
unmatchedVarParam*, firstMismatch*: int unmatchedVarParam*, firstMismatch*: int
diagnostics*: seq[string] diagnostics*: seq[string]
enabled*: bool
CandidateErrors* = seq[CandidateError] CandidateErrors* = seq[CandidateError]
@ -60,7 +61,8 @@ type
# matching. they will be reset if the matching # matching. they will be reset if the matching
# is not successful. may replace the bindings # is not successful. may replace the bindings
# table in the future. # table in the future.
diagnostics*: seq[string] # when this is not nil, the matching process diagnostics*: seq[string] # \
# when diagnosticsEnabled, the matching process
# will collect extra diagnostics that will be # will collect extra diagnostics that will be
# displayed to the user. # displayed to the user.
# triggered when overload resolution fails # triggered when overload resolution fails
@ -70,6 +72,7 @@ type
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
firstMismatch*: int # position of the first type mismatch for firstMismatch*: int # position of the first type mismatch for
# better error messages # better error messages
diagnosticsEnabled*: bool
TTypeRelFlag* = enum TTypeRelFlag* = enum
trDontBind trDontBind
@ -124,7 +127,8 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
idTablePut(c.bindings, key, val.skipIntLit) idTablePut(c.bindings, key, val.skipIntLit)
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
binding: PNode, calleeScope = -1, diagnostics = false) = binding: PNode, calleeScope = -1,
diagnosticsEnabled = false) =
initCandidateAux(ctx, c, callee.typ) initCandidateAux(ctx, c, callee.typ)
c.calleeSym = callee c.calleeSym = callee
if callee.kind in skProcKinds and calleeScope == -1: if callee.kind in skProcKinds and calleeScope == -1:
@ -139,7 +143,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeScope = 1 c.calleeScope = 1
else: else:
c.calleeScope = calleeScope c.calleeScope = calleeScope
c.diagnostics = if diagnostics: @[] else: nil c.diagnostics = if diagnosticsEnabled: @[] else: nil
c.diagnosticsEnabled = diagnosticsEnabled
c.magic = c.calleeSym.magic c.magic = c.calleeSym.magic
initIdTable(c.bindings) initIdTable(c.bindings)
if binding != nil and callee.kind in routineKinds: if binding != nil and callee.kind in routineKinds:
@ -724,7 +729,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
diagnostics: seq[string] diagnostics: seq[string]
errorPrefix: string errorPrefix: string
flags: TExprFlags = {} flags: TExprFlags = {}
collectDiagnostics = m.diagnostics != nil or collectDiagnostics = m.diagnosticsEnabled or
sfExplain in typeClass.sym.flags sfExplain in typeClass.sym.flags
if collectDiagnostics: if collectDiagnostics:
@ -743,7 +748,9 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
if collectDiagnostics: if collectDiagnostics:
writelnHook = oldWriteHook writelnHook = oldWriteHook
for msg in diagnostics: m.diagnostics.safeAdd msg for msg in diagnostics:
m.diagnostics.safeAdd msg
m.diagnosticsEnabled = true
if checkedBody == nil: return nil if checkedBody == nil: return nil
@ -1395,7 +1402,12 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
var aAsObject = roota.lastSon var aAsObject = roota.lastSon
if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind: if fKind in {tyRef, tyPtr}:
if aAsObject.kind == tyObject:
# bug #7600, tyObject cannot be passed
# as argument to tyRef/tyPtr
return isNone
elif aAsObject.kind == fKind:
aAsObject = aAsObject.base aAsObject = aAsObject.base
if aAsObject.kind == tyObject: if aAsObject.kind == tyObject:

View file

@ -208,7 +208,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add mapTypeToAst(t.sons[0], info) result.add mapTypeToAst(t.sons[0], info)
else: else:
result = mapTypeToBracket("ref", mRef, t, info) result = mapTypeToBracket("ref", mRef, t, info)
of tyVar: result = mapTypeToBracket("var", mVar, t, info) of tyVar:
if inst:
result = newNodeX(nkVarTy)
result.add mapTypeToAst(t.sons[0], info)
else:
result = mapTypeToBracket("var", mVar, t, info)
of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info) of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info) of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)

View file

@ -1553,6 +1553,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkFloatLit, info, t) result = newNodeIT(nkFloatLit, info, t)
of tyCString, tyString: of tyCString, tyString:
result = newNodeIT(nkStrLit, info, t) result = newNodeIT(nkStrLit, info, t)
result.strVal = ""
of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr, of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)

View file

@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex,
let bounds = match.matchBounds let bounds = match.matchBounds
result.add(str.substr(lastIdx, bounds.a - 1)) result.add(str.substr(lastIdx, bounds.a - 1))
let nextVal = replacement let nextVal = replacement
assert(nextVal != nil)
result.add(nextVal) result.add(nextVal)
lastIdx = bounds.b + 1 lastIdx = bounds.b + 1

View file

@ -1280,15 +1280,13 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {.
## sets the length of `s` to `newlen`. ## sets the length of `s` to `newlen`.
## ``T`` may be any sequence type. ## ``T`` may be any sequence type.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with ## ``s`` will be truncated.
## a size, use ``newSeq`` instead.
proc setLen*(s: var string, newlen: Natural) {. proc setLen*(s: var string, newlen: Natural) {.
magic: "SetLengthStr", noSideEffect.} magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`. ## sets the length of `s` to `newlen`.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a string with ## ``s`` will be truncated.
## a size, use ``newString`` instead.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var myS = "Nim is great!!" ## var myS = "Nim is great!!"
@ -2414,6 +2412,7 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
if seqToPtr(x) == seqToPtr(y): if seqToPtr(x) == seqToPtr(y):
return true return true
when not defined(nimNoNil):
if x.isNil or y.isNil: if x.isNil or y.isNil:
return false return false
@ -4012,18 +4011,18 @@ proc addQuoted*[T](s: var string, x: T) =
when hasAlloc: when hasAlloc:
# XXX: make these the default (or implement the NilObject optimization) # XXX: make these the default (or implement the NilObject optimization)
proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} = proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case, ## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case,
## ``x`` becomes ``@[y]`` ## ``x`` becomes ``@[y]``
if x == nil: x = @[y] if x == nil: x = @[y]
else: x.add(y) else: x.add(y)
proc safeAdd*(x: var string, y: char) = proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""`` ## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""``
if x == nil: x = "" if x == nil: x = ""
x.add(y) x.add(y)
proc safeAdd*(x: var string, y: string) = proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that ## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that
## case, ``x`` becomes ``y`` ## case, ``x`` becomes ``y``
if x == nil: x = y if x == nil: x = y

View file

@ -22,21 +22,34 @@ proc resize(old: int): int {.inline.} =
proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} = proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
if a == b: return 0 if a == b: return 0
when defined(nimNoNil):
let alen = if a == nil: 0 else: a.len
let blen = if b == nil: 0 else: b.len
else:
if a == nil: return -1 if a == nil: return -1
if b == nil: return 1 if b == nil: return 1
let minlen = min(a.len, b.len) let alen = a.len
let blen = b.len
let minlen = min(alen, blen)
if minlen > 0: if minlen > 0:
result = c_memcmp(addr a.data, addr b.data, minlen.csize) result = c_memcmp(addr a.data, addr b.data, minlen.csize)
if result == 0: if result == 0:
result = a.len - b.len result = alen - blen
else: else:
result = a.len - b.len result = alen - blen
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} = proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
if a == b: return true if a == b: return true
when defined(nimNoNil):
let alen = if a == nil: 0 else: a.len
let blen = if b == nil: 0 else: b.len
else:
if a == nil or b == nil: return false if a == nil or b == nil: return false
return a.len == b.len and let alen = a.len
equalMem(addr(a.data), addr(b.data), a.len) let blen = b.len
if alen == blen:
if alen == 0: return true
return equalMem(addr(a.data), addr(b.data), alen)
when declared(allocAtomic): when declared(allocAtomic):
template allocStr(size: untyped): untyped = template allocStr(size: untyped): untyped =
@ -101,9 +114,6 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
if str == nil: NimString(nil) if str == nil: NimString(nil)
else: toNimStr(str, str.len) else: toNimStr(str, str.len)
template wasMoved(x: NimString): bool = false
# (x.reserved and seqShallowFlag) != 0
proc copyString(src: NimString): NimString {.compilerRtl.} = proc copyString(src: NimString): NimString {.compilerRtl.} =
if src != nil: if src != nil:
if (src.reserved and seqShallowFlag) != 0: if (src.reserved and seqShallowFlag) != 0:
@ -161,14 +171,16 @@ proc hashString(s: string): int {.compilerproc.} =
proc addChar(s: NimString, c: char): NimString = proc addChar(s: NimString, c: char): NimString =
# is compilerproc! # is compilerproc!
if s == nil:
result = rawNewStringNoInit(1)
result.len = 0
else:
result = s result = s
if result.len >= result.space: if result.len >= result.space:
let r = resize(result.space) let r = resize(result.space)
result = cast[NimString](growObj(result, result = cast[NimString](growObj(result,
sizeof(TGenericSeq) + r + 1)) sizeof(TGenericSeq) + r + 1))
result.reserved = r result.reserved = r
elif wasMoved(s):
result = newOwnedString(s, s.len)
result.data[result.len] = c result.data[result.len] = c
result.data[result.len+1] = '\0' result.data[result.len+1] = '\0'
inc(result.len) inc(result.len)
@ -205,7 +217,9 @@ proc addChar(s: NimString, c: char): NimString =
# s = rawNewString(0); # s = rawNewString(0);
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
if dest.len + addlen <= dest.space and not wasMoved(dest): if dest == nil:
result = rawNewStringNoInit(addlen)
elif dest.len + addlen <= dest.space:
result = dest result = dest
else: # slow path: else: # slow path:
var sp = max(resize(dest.space), dest.len + addlen) var sp = max(resize(dest.space), dest.len + addlen)
@ -216,6 +230,7 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
# DO NOT UPDATE LEN YET: dest.len = newLen # DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} = proc appendString(dest, src: NimString) {.compilerproc, inline.} =
if src != nil:
copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1) copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
inc(dest.len, src.len) inc(dest.len, src.len)
@ -226,8 +241,8 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
var n = max(newLen, 0) var n = max(newLen, 0)
if wasMoved(s): if s == nil:
result = newOwnedString(s, n) result = mnewString(newLen)
elif n <= s.space: elif n <= s.space:
result = s result = s
else: else:
@ -261,6 +276,18 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
GenericSeqSize)) GenericSeqSize))
result.reserved = r result.reserved = r
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
if s == nil:
result = cast[PGenericSeq](newSeq(typ, 1))
result.len = 0
else:
result = s
if result.len >= result.space:
let r = resize(result.space)
result = cast[PGenericSeq](growObj(result, typ.base.size * r +
GenericSeqSize))
result.reserved = r
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
compilerRtl, inl.} = compilerRtl, inl.} =
result = seq result = seq
@ -301,6 +328,13 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
(newLen*%elemSize)), (result.len-%newLen) *% elemSize) (newLen*%elemSize)), (result.len-%newLen) *% elemSize)
result.len = newLen result.len = newLen
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
compilerRtl.} =
if s == nil:
result = cast[PGenericSeq](newSeq(typ, newLen))
else:
result = setLengthSeq(s, typ.base.size, newLen)
# --------------- other string routines ---------------------------------- # --------------- other string routines ----------------------------------
proc add*(result: var string; x: int64) = proc add*(result: var string; x: int64) =
let base = result.len let base = result.len

View file

@ -0,0 +1,51 @@
discard """
output: '''apple
banana
Fruit
2
4
3
none
skin
paper
'''
"""
type
Fruit = object of RootObj
name: string
Apple = object of Fruit
Banana = object of Fruit
var
ir = Fruit(name: "Fruit")
ia = Apple(name: "apple")
ib = Banana(name: "banana")
let x = [ia.addr, ib.addr, ir.addr]
for c in x: echo c.name
type
Vehicle[T] = object of RootObj
tire: T
Car[T] = object of Vehicle[T]
Bike[T] = object of Vehicle[T]
var v = Vehicle[int](tire: 3)
var c = Car[int](tire: 4)
var b = Bike[int](tire: 2)
let y = [b.addr, c.addr, v.addr]
for c in y: echo c.tire
type
Book[T] = ref object of RootObj
cover: T
Hard[T] = ref object of Book[T]
Soft[T] = ref object of Book[T]
var bn = Book[string](cover: "none")
var hs = Hard[string](cover: "skin")
var bp = Soft[string](cover: "paper")
let z = [bn, hs, bp]
for c in z: echo c.cover

View file

@ -0,0 +1,17 @@
discard """
file: "tarraycons_ptr_generic2.nim"
line: 17
errormsg: "type mismatch: got <ptr Hard[system.string]> but expected 'Book[system.string]'"
"""
type
Book[T] = ref object of RootObj
cover: T
Hard[T] = ref object of Book[T]
Soft[T] = ref object of Book[T]
var bn = Book[string](cover: "none")
var hs = Hard[string](cover: "skin")
var bp = Soft[string](cover: "paper")
let z = [bn, hs.addr, bp]

View file

@ -3,7 +3,7 @@ discard """
exitcode: 1 exitcode: 1
""" """
proc returnsNil(): string = return nil proc returnsNil(): ref int = return nil
iterator fields*(a, b: int): int = iterator fields*(a, b: int): int =
if a == b: if a == b:
@ -17,6 +17,6 @@ proc main(): string =
result = "" result = ""
for i in fields(0, 1): for i in fields(0, 1):
let x = returnsNil() let x = returnsNil()
result &= "string literal " & $x result &= "string literal " & $x[]
echo main() echo main()

View file

@ -209,7 +209,7 @@ proc `$`*(data: Tcommandline_results): string =
# - Parse code # - Parse code
template raise_or_quit(exception, message: expr): stmt {.immediate.} = template raise_or_quit(exception, message: untyped) =
## Avoids repeating if check based on the default quit_on_failure variable. ## Avoids repeating if check based on the default quit_on_failure variable.
## ##
## As a special case, if message has a zero length the call to quit won't ## As a special case, if message has a zero length the call to quit won't
@ -230,15 +230,15 @@ template run_custom_proc(parsed_parameter: Tparsed_parameter,
## ##
## Pass in the string of the parameter triggering the call. If the ## Pass in the string of the parameter triggering the call. If the
if not custom_validator.isNil: if not custom_validator.isNil:
except: try:
raise_or_quit(ValueError, ("Couldn't run custom proc for " &
"parameter $1:\n$2" % [escape(parameter),
getCurrentExceptionMsg()]))
let message = custom_validator(parameter, parsed_parameter) let message = custom_validator(parameter, parsed_parameter)
if not message.isNil and message.len > 0: if not message.isNil and message.len > 0:
raise_or_quit(ValueError, ("Failed to validate value for " & raise_or_quit(ValueError, ("Failed to validate value for " &
"parameter $1:\n$2" % [escape(parameter), message])) "parameter $1:\n$2" % [escape(parameter), message]))
except:
raise_or_quit(ValueError, ("Couldn't run custom proc for " &
"parameter $1:\n$2" % [escape(parameter),
getCurrentExceptionMsg()]))
proc parse_parameter(quit_on_failure: bool, param, value: string, proc parse_parameter(quit_on_failure: bool, param, value: string,
param_kind: Tparam_kind): Tparsed_parameter = param_kind: Tparam_kind): Tparsed_parameter =

View file

@ -0,0 +1,25 @@
discard """
output: '''@[nil, nil, nil, nil, nil, nil, nil, "meh"]'''
exitcode: "0"
"""
when true:
var ab: string
ab &= "more"
doAssert ab == "more"
var x: seq[string]
setLen(x, 7)
x.add "meh"
var s: string
var z = "abc"
var zz: string
s &= "foo" & z & zz
doAssert s == "fooabc"
echo x

View file

@ -51,3 +51,59 @@ import strutils
let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0'] let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']" doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']"
doAssert $cstring(unsafeAddr arr) == "Hello World!" doAssert $cstring(unsafeAddr arr) == "Hello World!"
proc takes(c: cstring) =
doAssert c == ""
proc testm() =
var x: string
# nil is mapped to "":
takes(x)
testm()
# nil tests
var xx: seq[string]
var yy: string
doAssert xx == @[]
doAssert yy == ""
proc bar(arg: cstring): void =
doAssert arg[0] == '\0'
proc baz(arg: openarray[char]): void =
doAssert arg.len == 0
proc stringCompare(): void =
var a,b,c,d,e,f,g: string
a.add 'a'
doAssert a == "a"
b.add "bee"
doAssert b == "bee"
b.add g
doAssert b == "bee"
c.add 123.456
doAssert c == "123.456"
d.add 123456
doAssert d == "123456"
doAssert e == ""
doAssert "" == e
doAssert nil == e
doAssert e == nil
doAssert f == g
doAssert "" == ""
doAssert "" == nil
doAssert nil == ""
g.setLen(10)
doAssert g == "\0\0\0\0\0\0\0\0\0\0"
doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
var nilstring: string
bar(nilstring)
baz(nilstring)
stringCompare()
static:
stringCompare()

18
tests/typerel/t7600_1.nim Normal file
View file

@ -0,0 +1,18 @@
discard """
errormsg: "type mismatch: got <Thin[system.int]>"
nimout: '''t7600_1.nim(18, 6) Error: type mismatch: got <Thin[system.int]>
but expected one of:
proc test[T](x: Paper[T])
expression: test tn'''
"""
type
Paper[T] = ref object of RootObj
thickness: T
Thin[T] = object of Paper[T]
proc test[T](x: Paper[T]) = discard
var tn = Thin[int]()
test tn

17
tests/typerel/t7600_2.nim Normal file
View file

@ -0,0 +1,17 @@
discard """
errormsg: "type mismatch: got <Thin>"
nimout: '''t7600_2.nim(17, 6) Error: type mismatch: got <Thin>
but expected one of:
proc test(x: Paper)
expression: test tn'''
"""
type
Paper = ref object of RootObj
Thin = object of Paper
proc test(x: Paper) = discard
var tn = Thin()
test tn

View file

@ -128,7 +128,7 @@ proc highlight(s, match, repl: string, t: tuple[first, last: int],
stdout.write("\n") stdout.write("\n")
stdout.flushFile() stdout.flushFile()
proc processFile(pattern; filename: string) = proc processFile(pattern; filename: string; counter: var int) =
var filenameShown = false var filenameShown = false
template beforeHighlight = template beforeHighlight =
if not filenameShown and optVerbose notin options and not oneline: if not filenameShown and optVerbose notin options and not oneline:
@ -166,6 +166,7 @@ proc processFile(pattern; filename: string) =
var wholeMatch = buffer.substr(t.first, t.last) var wholeMatch = buffer.substr(t.first, t.last)
beforeHighlight() beforeHighlight()
inc counter
if optReplace notin options: if optReplace notin options:
highlight(buffer, wholeMatch, "", t, filename, line, showRepl=false) highlight(buffer, wholeMatch, "", t, filename, line, showRepl=false)
else: else:
@ -241,17 +242,17 @@ proc styleInsensitive(s: string): string =
addx() addx()
else: addx() else: addx()
proc walker(pattern; dir: string) = proc walker(pattern; dir: string; counter: var int) =
for kind, path in walkDir(dir): for kind, path in walkDir(dir):
case kind case kind
of pcFile: of pcFile:
if extensions.len == 0 or path.hasRightExt(extensions): if extensions.len == 0 or path.hasRightExt(extensions):
processFile(pattern, path) processFile(pattern, path, counter)
of pcDir: of pcDir:
if optRecursive in options: if optRecursive in options:
walker(pattern, path) walker(pattern, path, counter)
else: discard else: discard
if existsFile(dir): processFile(pattern, dir) if existsFile(dir): processFile(pattern, dir, counter)
proc writeHelp() = proc writeHelp() =
stdout.write(Usage) stdout.write(Usage)
@ -321,6 +322,7 @@ if optStdin in options:
if pattern.len == 0: if pattern.len == 0:
writeHelp() writeHelp()
else: else:
var counter = 0
if filenames.len == 0: if filenames.len == 0:
filenames.add(os.getCurrentDir()) filenames.add(os.getCurrentDir())
if optRegex notin options: if optRegex notin options:
@ -332,7 +334,7 @@ else:
pattern = "\\i " & pattern pattern = "\\i " & pattern
let pegp = peg(pattern) let pegp = peg(pattern)
for f in items(filenames): for f in items(filenames):
walker(pegp, f) walker(pegp, f, counter)
else: else:
var reflags = {reStudy, reExtended} var reflags = {reStudy, reExtended}
if optIgnoreStyle in options: if optIgnoreStyle in options:
@ -343,5 +345,6 @@ else:
reflags.incl reIgnoreCase reflags.incl reIgnoreCase
let rep = re(pattern, reflags) let rep = re(pattern, reflags)
for f in items(filenames): for f in items(filenames):
walker(rep, f) walker(rep, f, counter)
if not oneline:
stdout.write($counter & " matches\n")