diff options
author | Michael Voronin <survivor.mail@gmail.com> | 2018-05-03 17:12:01 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-03 17:12:01 +0300 |
commit | 5ea967d97a30f0084883d4efa81b05bea3e5d148 (patch) | |
tree | 05ea0e3624f6720c2f5af28b5a70c87c85feafc7 /compiler | |
parent | 3949c9f977378ea3ab2b3c750f4dc2bc8d853022 (diff) | |
parent | 5564289b577c620cbd775f477b7fc8b6507adbfa (diff) | |
download | Nim-5ea967d97a30f0084883d4efa81b05bea3e5d148.tar.gz |
Merge pull request #3 from nim-lang/devel
pull #3
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ccgcalls.nim | 8 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 52 | ||||
-rw-r--r-- | compiler/commands.nim | 17 | ||||
-rw-r--r-- | compiler/condsyms.nim | 4 | ||||
-rw-r--r-- | compiler/filter_tmpl.nim | 3 | ||||
-rw-r--r-- | compiler/lexer.nim | 15 | ||||
-rw-r--r-- | compiler/msgs.nim | 9 | ||||
-rw-r--r-- | compiler/nimfix/nimfix.nim | 14 | ||||
-rw-r--r-- | compiler/options.nim | 6 | ||||
-rw-r--r-- | compiler/renderer.nim | 13 | ||||
-rw-r--r-- | compiler/ropes.nim | 6 | ||||
-rw-r--r-- | compiler/sem.nim | 5 | ||||
-rw-r--r-- | compiler/semcall.nim | 27 | ||||
-rw-r--r-- | compiler/semdata.nim | 2 | ||||
-rw-r--r-- | compiler/semexprs.nim | 10 | ||||
-rw-r--r-- | compiler/semfold.nim | 96 | ||||
-rw-r--r-- | compiler/semstmts.nim | 210 | ||||
-rw-r--r-- | compiler/semtypes.nim | 2 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 26 | ||||
-rw-r--r-- | compiler/syntaxes.nim | 10 | ||||
-rw-r--r-- | compiler/types.nim | 12 | ||||
-rw-r--r-- | compiler/vmdeps.nim | 7 | ||||
-rw-r--r-- | compiler/vmgen.nim | 1 |
23 files changed, 278 insertions, 277 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 5f14b6804..2ddc88509 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -125,15 +125,15 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar and 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: - result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)] + result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))] of tyPtr, tyRef: case lastSon(a.t).kind 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: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))] else: @@ -143,7 +143,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} = var a: TLoc 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 = var a: TLoc diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ea373f5a6..96f9265f1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -63,6 +63,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = of tyNil: result = genNilStringLiteral(p.module, n.info) of tyString: + # with the new semantics for 'nil' strings, we can map "" to nil and + # save tons of allocations: + #if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info) + #else: result = genStringLiteral(p.module, n) else: if n.strVal.isNil: result = rope("NIM_NIL") @@ -882,7 +886,7 @@ proc genIndexCheck(p: BProc; arr, idx: TLoc) = rdCharLoc(idx), first, intLiteral(lastOrd(ty))) of tySequence, tyString: 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)) else: discard @@ -905,14 +909,14 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: if optBoundsCheck in p.options: - if ty.kind == tyString: + if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): linefmt(p, cpsStmts, - "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", + rdLoc(b), rdLoc(a), lenField(p)) else: linefmt(p, cpsStmts, - "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", + rdLoc(b), rdLoc(a), lenField(p)) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = rfmt(nil, "(*$1)", a.r) @@ -980,10 +984,10 @@ proc genEcho(p: BProc, n: PNode) = var a: TLoc for it in n.sons: if it.skipConv.kind == nkNilLit: - add(args, ", \"nil\"") + add(args, ", \"\"") else: 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>") linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) else: @@ -1034,7 +1038,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 1].strVal)) 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))) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L)) add(p.s(cpsStmts), appends) @@ -1073,7 +1077,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 2].strVal)) 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", rdLoc(dest), rdLoc(a))) linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", @@ -1086,17 +1090,17 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; let seqAppendPattern = if not p.module.compileToCpp: - "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n" + "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n" else: - "$1 = ($2) #incrSeqV2($1, sizeof($3));$n" + "$1 = ($2) #incrSeqV3($1, $3);$n" var a, b, dest, tmpL: TLoc initLocExpr(p, e.sons[1], a) 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, [ rdLoc(a), getTypeDesc(p.module, e.sons[1].typ), - getTypeDesc(p.module, bt)]) + genTypeInfo(p.module, seqType, e.info)]) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) initLoc(dest, locExpr, e.sons[2], OnHeap) @@ -1417,7 +1421,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: 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: putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage) @@ -1500,13 +1504,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[2], b) let t = skipTypes(e.sons[1].typ, {tyVar}) 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: - "$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n" + "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n" lineCg(p, cpsStmts, setLenPattern, [ 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) proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = @@ -1740,7 +1744,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc 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) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = @@ -1755,16 +1759,14 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = var x: TLoc var a = e.sons[1] var b = e.sons[2] - if (a.kind == nkNilLit) or (b.kind == nkNilLit): - binaryExpr(p, e, d, "($1 == $2)") - elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""): + if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": initLocExpr(p, e.sons[2], x) putIntoDest(p, d, e, - rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) - elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""): + rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": initLocExpr(p, e.sons[1], x) 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: binaryExpr(p, e, d, "#eqStrings($1, $2)") diff --git a/compiler/commands.nim b/compiler/commands.nim index 06b487cf0..d8d8ae4b7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -54,6 +54,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; const HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" & + "Compiled at $4 $5\n" & "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n" const @@ -68,7 +69,8 @@ const proc getCommandLineDesc(): string = result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]) & Usage + CPU[platform.hostCPU].name, CompileDate, CompileTime]) & + Usage proc helpOnError(pass: TCmdLinePass) = if pass == passCmd1: @@ -79,7 +81,8 @@ proc writeAdvancedUsage(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]) & AdvancedUsage, + CPU[platform.hostCPU].name, CompileDate, CompileTime]) & + AdvancedUsage, {msgStdout}) msgQuit(0) @@ -87,7 +90,8 @@ proc writeFullhelp(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]) & Usage & AdvancedUsage, + CPU[platform.hostCPU].name, CompileDate, CompileTime]) & + Usage & AdvancedUsage, {msgStdout}) msgQuit(0) @@ -95,7 +99,7 @@ proc writeVersionInfo(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]), + CPU[platform.hostCPU].name, CompileDate, CompileTime]), {msgStdout}) const gitHash = gorge("git log -n 1 --format=%H").strip @@ -188,11 +192,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, if i < len(arg) and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(pass, orig, info) if state == wHint: - var x = findStr(msgs.HintsToStr, id) + let x = findStr(msgs.HintsToStr, id) if x >= 0: n = TNoteKind(x + ord(hintMin)) else: localError(info, "unknown hint: " & id) else: - var x = findStr(msgs.WarningsToStr, id) + let x = findStr(msgs.WarningsToStr, id) if x >= 0: n = TNoteKind(x + ord(warnMin)) else: localError(info, "unknown warning: " & id) case substr(arg, i).normalize @@ -499,6 +503,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; undefSymbol("nimOldNewlines") else: localError(info, errOnOrOffExpectedButXFound, arg) + of "laxstrings": processOnOffSwitch({optLaxStrings}, arg, pass, info) of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info) of "floatchecks": processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 028aedb5b..f8a75e68e 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -82,7 +82,6 @@ proc countDefinedSymbols*(): int = proc initDefines*() = gSymbols = newStringTable(modeStyleInsensitive) - defineSymbol("nimrod") # 'nimrod' is always defined # for bootstrapping purposes and old code: defineSymbol("nimhygiene") defineSymbol("niminheritable") @@ -115,3 +114,6 @@ proc initDefines*() = defineSymbol("nimHasNilChecks") defineSymbol("nimSymKind") defineSymbol("nimVmEqIdent") + defineSymbol("nimNoNil") + defineSymbol("nimNoZeroTerminator") + defineSymbol("nimNotNil") diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index a1ba9113c..51ccb8390 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -42,7 +42,8 @@ proc newLine(p: var TTmplParser) = proc scanPar(p: var TTmplParser, d: int) = var i = d - while true: + let hi = p.x.len - 1 + while i <= hi: case p.x[i] of '\0': break of '(': inc(p.par) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0b1090bb1..0478ed574 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -165,13 +165,12 @@ proc isKeyword*(kind: TTokType): bool = template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion proc isNimIdentifier*(s: string): bool = - if s[0] in SymStartChars: + let sLen = s.len + if sLen > 0 and s[0] in SymStartChars: var i = 1 - var sLen = s.len while i < sLen: - if s[i] == '_': - inc(i) - if s[i] notin SymChars: return + if s[i] == '_': inc(i) + if i < sLen and s[i] notin SymChars: return inc(i) result = true @@ -311,12 +310,12 @@ template tokenEndPrevious(tok, pos) = # We need to parse the largest uint literal without overflow checks proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int = var i = start - if s[i] in {'0'..'9'}: + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and s[i] in {'0'..'9'}: b = b * 10 + (ord(s[i]) - ord('0')) inc(i) - while s[i] == '_': inc(i) # underscores are allowed and ignored + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored result = i - start {.pop.} # overflowChecks diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 5ae2c4970..5da375c1c 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -126,7 +126,7 @@ type warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnUnknownSubstitutionX, warnLanguageXNotSupported, warnFieldXNotSupported, warnCommentXIgnored, - warnNilStatement, warnTypelessParam, + warnTypelessParam, warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, @@ -400,7 +400,6 @@ const warnLanguageXNotSupported: "language \'$1\' not supported", warnFieldXNotSupported: "field \'$1\' not supported", warnCommentXIgnored: "comment \'$1\' ignored", - warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead", warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", warnWriteToForeignHeap: "write to foreign heap", @@ -451,7 +450,7 @@ const "SmallLshouldNotBeUsed", "UnknownMagic", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "FieldXNotSupported", - "CommentXIgnored", "NilStmt", + "CommentXIgnored", "TypelessParam", "UseBase", "WriteToForeignHeap", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", @@ -474,6 +473,10 @@ const hintMin* = hintSuccess hintMax* = high(TMsgKind) +static: + doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 + doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 + type TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKinds* = set[TNoteKind] diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index a97d88078..2ef375b00 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -47,7 +47,7 @@ proc mainCommand = compileProject(newModuleGraph(), newIdentCache()) pretty.overwriteFiles() -proc processCmdLine*(pass: TCmdLinePass, cmd: string) = +proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) = var p = parseopt.initOptParser(cmd) var argsCount = 0 gOnlyMainfile = true @@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = of "wholeproject": gOnlyMainfile = false of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error else: - processSwitch(pass, p) + processSwitch(pass, p, config) of cmdArgument: options.gProjectName = unixToNativePath(p.key) # if processArgument(pass, p, argsCount): break -proc handleCmdLine() = +proc handleCmdLine(config: ConfigRef) = if paramCount() == 0: stdout.writeLine(Usage) else: - processCmdLine(passCmd1, "") + processCmdLine(passCmd1, "", config) if gProjectName != "": try: gProjectFull = canonicalizePath(gProjectName) @@ -96,11 +96,11 @@ proc handleCmdLine() = gProjectName = p.name else: 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 # command line can overwite the config file's settings extccomp.initVars() - processCmdLine(passCmd2, "") + processCmdLine(passCmd2, "", config) mainCommand() when compileOption("gc", "v2") or compileOption("gc", "refc"): @@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"): condsyms.initDefines() defineSymbol "nimfix" -handleCmdline() +handleCmdline newConfigRef() diff --git a/compiler/options.nim b/compiler/options.nim index a5dfaa81c..5baaa1bfd 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -37,7 +37,8 @@ type # please make sure we have under 32 options # evaluation optPatterns, # en/disable pattern matching optMemTracker, - optHotCodeReloading + optHotCodeReloading, + optLaxStrings TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** @@ -108,7 +109,8 @@ type dotOperators, callOperator, parallel, - destructor + destructor, + notnil ConfigRef* = ref object ## eventually all global configuration should be moved here cppDefines*: HashSet[string] diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 2a65a10a8..95a622d4e 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -175,10 +175,11 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) = proc putComment(g: var TSrcGen, s: string) = if s.isNil: return var i = 0 + let hi = len(s) - 1 var isCode = (len(s) >= 2) and (s[1] != ' ') var ind = g.lineLen var com = "## " - while true: + while i <= hi: case s[i] of '\0': break @@ -201,12 +202,12 @@ proc putComment(g: var TSrcGen, s: string) = # gets too long: # compute length of the following word: var j = i - while s[j] > ' ': inc(j) + while j <= hi and s[j] > ' ': inc(j) if not isCode and (g.lineLen + (j - i) > MaxLineLen): put(g, tkComment, com) optNL(g, ind) com = "## " - while s[i] > ' ': + while i <= hi and s[i] > ' ': add(com, s[i]) inc(i) put(g, tkComment, com) @@ -215,8 +216,9 @@ proc putComment(g: var TSrcGen, s: string) = proc maxLineLength(s: string): int = if s.isNil: return 0 var i = 0 + let hi = len(s) - 1 var lineLen = 0 - while true: + while i <= hi: case s[i] of '\0': break @@ -235,7 +237,7 @@ proc maxLineLength(s: string): int = proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = var i = 0 - var hi = len(s) - 1 + let hi = len(s) - 1 var str = "" while i <= hi: case s[i] @@ -1064,6 +1066,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if n.len > 1: let opr = if n[0].kind == nkIdent: n[0].ident elif n[0].kind == nkSym: n[0].sym.name + elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name else: nil if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)): put(g, tkSpaces, Space) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 358ce8a53..05d5e840c 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -188,11 +188,11 @@ iterator leaves*(r: Rope): string = var stack = @[r] while stack.len > 0: var it = stack.pop - while isNil(it.data): + while it.left != nil: + assert it.right != nil stack.add(it.right) it = it.left assert(it != nil) - assert(it.data != nil) yield it.data iterator items*(r: Rope): char = @@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) - if frmt[i] notin {'0'..'9'}: break + if i >= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(args) + 1: errorHandler(rInvalidFormatStr, $(j)) diff --git a/compiler/sem.nim b/compiler/sem.nim index 041f2e127..52282d0e4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -153,8 +153,9 @@ proc commonType*(x, y: PType): PType = if a.kind in {tyRef, tyPtr}: k = a.kind if b.kind != a.kind: return x - a = a.lastSon - b = b.lastSon + # bug #7601, array construction of ptr generic + a = a.lastSon.skipTypes({tyGenericInst}) + b = b.lastSon.skipTypes({tyGenericInst}) if a.kind == tyObject and b.kind == tyObject: result = commonSuperclass(a, b) # this will trigger an error later: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index f443339f5..aa53fda3b 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, filter: TSymKinds, best, alt: var TCandidate, errors: var CandidateErrors, - diagnosticsFlag = false) = + diagnosticsFlag: bool, + errorsEnabled: bool) = var o: TOverloadIter var sym = initOverloadIter(o, c, headSymbol) 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))' let counterInitial = c.currentScope.symbols.counter var syms: seq[tuple[s: PSym, scope: int]] + var noSyms = true var nextSymIndex = 0 while sym != nil: if sym.kind in filter: @@ -102,7 +104,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, var cmp = cmpCandidates(best, z) 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 errors != nil or z.diagnostics != nil: + elif errorsEnabled or z.diagnosticsEnabled: errors.safeAdd(CandidateError( sym: sym, 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. syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, best, alt, o, diagnosticsFlag) - if syms == nil: + noSyms = false + if noSyms: sym = nextOverloadIter(o, c, headSymbol) scope = o.lastOverloadScope elif nextSymIndex < syms.len: @@ -206,7 +209,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = if errorOutputs == {}: # fail fast: globalError(n.info, errTypeMismatch, "") - if errors.isNil or errors.len == 0: + if errors.len == 0: localError(n.info, errExprXCannotBeCalled, n[0].renderTree) return @@ -227,7 +230,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) = if symx.kind in routineKinds: errors.add(CandidateError(sym: symx, unmatchedVarParam: 0, firstMismatch: 0, - diagnostics: nil)) + diagnostics: nil, + enabled: false)) symx = nextOverloadIter(o, c, headSymbol) if errors.len == 0: localError(n.info, "could not resolve: " & $n) @@ -236,7 +240,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) = proc resolveOverloads(c: PContext, n, orig: PNode, filter: TSymKinds, flags: TExprFlags, - errors: var CandidateErrors): TCandidate = + errors: var CandidateErrors, + errorsEnabled: bool): TCandidate = var initialBinding: PNode var alt: TCandidate var f = n.sons[0] @@ -249,7 +254,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, template pickBest(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, - filter, result, alt, errors, efExplain in flags) + filter, result, alt, errors, efExplain in flags, + errorsEnabled) pickBest(f) let overloadsState = result.state @@ -423,9 +429,8 @@ proc tryDeref(n: PNode): PNode = proc semOverloadedCall(c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags): PNode = - var errors: CandidateErrors = if efExplain in flags: @[] - else: nil - var r = resolveOverloads(c, n, nOrig, filter, flags, errors) + var errors: CandidateErrors = if efExplain in flags: @[] else: nil + var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: # this may be triggered, when the explain pragma is used if errors.len > 0: @@ -443,7 +448,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # into sigmatch with hidden conversion produced there # 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) else: # get rid of the deref again for a better error message: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8159abf8f..fc0488814 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -63,7 +63,7 @@ type # to the user. efWantStmt, efAllowStmt, efDetermineType, efExplain, efAllowDestructor, efWantValue, efOperand, efNoSemCheck, - efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo, + efNoEvaluateGeneric, efInCall, efFromHlo TExprFlags* = set[TExprFlag] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6ad5d931d..feca087fc 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -51,7 +51,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = renderTree(result, {renderNoComments})) result.typ = errorType(c) else: - if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) 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, renderTree(result, {renderNoComments})) result.typ = errorType(c) - else: - semProcvarCheck(c, result) proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) @@ -308,7 +305,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = maybeLiftType(t2, c, n.info) var m: TCandidate 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 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 # calls and then they wouldn't be analysed otherwise analyseIfAddressTakenInCall(c, n.sons[i]) - semProcvarCheck(c, n.sons[i]) if i < sonsLen(t) and skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: if n.sons[i].kind != nkHiddenAddr: @@ -1245,7 +1243,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = checkMinSonsLen(n, 2) # make sure we don't evaluate generic macros/templates n.sons[0] = semExprWithType(c, n.sons[0], - {efNoProcvarCheck, efNoEvaluateGeneric}) + {efNoEvaluateGeneric}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) case arr.kind diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 6fcc9a0a4..c4d79a4a3 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -13,7 +13,7 @@ import strutils, options, ast, astalgo, trees, treetab, nimsets, times, nversion, platform, math, msgs, os, condsyms, idents, renderer, types, - commands, magicsys, saturate + commands, magicsys proc getConstExpr*(m: PSym, n: PNode): PNode # evaluates the constant expression or returns nil if it is no constant @@ -24,6 +24,63 @@ proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode proc newStrNodeT*(strVal: string, n: PNode): PNode +proc checkInRange(n: PNode, res: BiggestInt): bool = + if res in firstOrd(n.typ)..lastOrd(n.typ): + result = true + +proc foldAdd(a, b: BiggestInt, n: PNode): PNode = + let res = a +% b + if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and + checkInRange(n, res): + result = newIntNodeT(res, n) + +proc foldSub*(a, b: BiggestInt, n: PNode): PNode = + let res = a -% b + if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and + checkInRange(n, res): + result = newIntNodeT(res, n) + +proc foldAbs*(a: BiggestInt, n: PNode): PNode = + if a != firstOrd(n.typ): + result = newIntNodeT(a, n) + +proc foldMod*(a, b: BiggestInt, n: PNode): PNode = + if b != 0'i64: + result = newIntNodeT(a mod b, n) + +proc foldModU*(a, b: BiggestInt, n: PNode): PNode = + if b != 0'i64: + result = newIntNodeT(a %% b, n) + +proc foldDiv*(a, b: BiggestInt, n: PNode): PNode = + if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64): + result = newIntNodeT(a div b, n) + +proc foldDivU*(a, b: BiggestInt, n: PNode): PNode = + if b != 0'i64: + result = newIntNodeT(a /% b, n) + +proc foldMul*(a, b: BiggestInt, n: PNode): PNode = + let res = a *% b + let floatProd = toBiggestFloat(a) * toBiggestFloat(b) + let resAsFloat = toBiggestFloat(res) + + # Fast path for normal case: small multiplicands, and no info + # is lost in either method. + if resAsFloat == floatProd and checkInRange(n, res): + return newIntNodeT(res, n) + + # Somebody somewhere lost info. Close enough, or way off? Note + # that a != 0 and b != 0 (else resAsFloat == floatProd == 0). + # The difference either is or isn't significant compared to the + # true value (of which floatProd is a good approximation). + + # abs(diff)/abs(prod) <= 1/32 iff + # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" + if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and + checkInRange(n, res): + return newIntNodeT(res, n) + # implementation proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode = @@ -172,23 +229,22 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away of mToFloat, mToBiggestFloat: result = newFloatNodeT(toFloat(int(getInt(a))), n) + # XXX: Hides overflow/underflow of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) - of mAbsI: - if getInt(a) >= 0: result = a - else: result = newIntNodeT(- getInt(a), n) + of mAbsI: result = foldAbs(getInt(a), n) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64 result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n) of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n) of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n) - of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n) - of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n) - of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n) - of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n) - of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n) - of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n) + of mUnaryLt: result = foldSub(getOrdValue(a), 1, n) + of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n) + of mPred: result = foldSub(getOrdValue(a), getInt(b), n) + of mAddI: result = foldAdd(getInt(a), getInt(b), n) + of mSubI: result = foldSub(getInt(a), getInt(b), n) + of mMulI: result = foldMul(getInt(a), getInt(b), n) of mMinI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(a), n) @@ -211,14 +267,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of tyInt64, tyInt, tyUInt..tyUInt64: result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) else: internalError(n.info, "constant folding for shr") - of mDivI: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`|div|`(getInt(a), y), n) - of mModI: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`|mod|`(getInt(a), y), n) + of mDivI: result = foldDiv(getInt(a), getInt(b), n) + of mModI: result = foldMod(getInt(a), getInt(b), n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) @@ -258,14 +308,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) - of mModU: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`%%`(getInt(a), y), n) - of mDivU: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`/%`(getInt(a), y), n) + of mModU: result = foldModU(getInt(a), getInt(b), n) + of mDivU: result = foldDivU(getInt(a), getInt(b), n) of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n) of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n) of mLtSet: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 94090852f..8b466f1da 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -71,37 +71,12 @@ proc toCover(t: PType): BiggestInt = else: 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 semExprBranch(c: PContext, n: PNode): PNode = result = semExpr(c, n) if result.typ != nil: # XXX tyGenericInst here? - semProcvarCheck(c, result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) proc semExprBranchScope(c: PContext, n: PNode): PNode = @@ -132,30 +107,23 @@ proc fixNilType(n: PNode) = proc discardCheck(c: PContext, result: PNode) = if c.matchedConcept != nil: return if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: - if result.kind == nkNilLit: - result.typ = nil - message(result.info, warnNilStatement) - elif implicitlyDiscardable(result): + if implicitlyDiscardable(result): var n = result result.typ = nil while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil elif result.typ.kind != tyError and gCmd != cmdInteractive: - if result.typ.kind == tyNil: - fixNilType(result) - message(result.info, warnNilStatement) - else: - var n = result - while n.kind in skipForDiscardable: n = n.lastSon - var s = "expression '" & $n & "' is of type '" & - result.typ.typeToString & "' and has to be discarded" - if result.info.line != n.info.line or - result.info.fileIndex != n.info.fileIndex: - s.add "; start of expression here: " & $result.info - if result.typ.kind == tyProc: - s.add "; for a function call use ()" - localError(n.info, s) + var n = result + while n.kind in skipForDiscardable: n = n.lastSon + var s = "expression '" & $n & "' is of type '" & + result.typ.typeToString & "' and has to be discarded" + if result.info.line != n.info.line or + result.info.fileIndex != n.info.fileIndex: + s.add "; start of expression here: " & $result.info + if result.typ.kind == tyProc: + s.add "; for a function call use ()" + localError(n.info, s) proc semIf(c: PContext, n: PNode): PNode = result = n @@ -384,7 +352,7 @@ proc checkNilable(v: PSym) = 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 value = identDefs[L-1] if result.kind == nkStmtList: @@ -525,12 +493,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = localError(a.info, errXExpected, "tuple") elif length-2 != sonsLen(tup): localError(a.info, errWrongNumberOfVariables) - else: - b = newNodeI(nkVarTuple, a.info) - newSons(b, length) - b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator - b.sons[length-1] = def - addToVarSection(c, result, n, b) + b = newNodeI(nkVarTuple, a.info) + newSons(b, length) + b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator + b.sons[length-1] = def + addToVarSection(c, result, n, b) elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and a.kind == nkIdentDefs and a.len > 3: message(a.info, warnEachIdentIsTuple) @@ -572,7 +539,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addToVarSection(c, result, n, b) else: 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) checkNilable(v) if sfCompileTime in v.flags: hasCompileTime = true @@ -1588,9 +1557,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: 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: - 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: # for DLL generation it is annoying to check for sfImportc! @@ -1682,11 +1653,6 @@ proc semIterator(c: PContext, n: PNode): PNode = incl(s.typ.flags, tfCapturesEnv) else: 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: localError(n.info, errImplOfXexpected, s.name.s) @@ -1792,14 +1758,6 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = evalStaticStmt(c.module, c.cache, a, c.p.owner) result = newNodeI(nkDiscardStmt, n.info, 1) 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 = # nkStmtList(expr) properly propagates the void context, @@ -1841,80 +1799,47 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # nkNilLit, nkEmpty}: # dec last 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) + var expr = semExpr(c, n.sons[i], flags) + n.sons[i] = expr + if c.matchedConcept != nil and expr.typ != nil and + (nfFromTemplate notin n.flags or i != last): + case expr.typ.kind + of tyBool: + if expr.kind == nkInfix and + expr[0].kind == nkSym and + expr[0].sym.name.s == "==": + if expr[1].typ.isUnresolvedStatic: + inferConceptStaticParam(c, expr[1], expr[2]) + continue + elif expr[2].typ.isUnresolvedStatic: + inferConceptStaticParam(c, expr[2], expr[1]) + continue + + let verdict = semConstExpr(c, n[i]) + if verdict.intVal == 0: + localError(result.info, "concept predicate failed") + of tyUnknown: continue + else: discard + if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): + voidContext = true + n.typ = enforceVoidContext + if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ - return + if not isEmptyType(n.typ): n.kind = nkStmtListExpr + elif i != last or voidContext: + discardCheck(c, n.sons[i]) else: - var expr = semExpr(c, n.sons[i], flags) - n.sons[i] = expr - if c.matchedConcept != nil and expr.typ != nil and - (nfFromTemplate notin n.flags or i != last): - case expr.typ.kind - of tyBool: - if expr.kind == nkInfix and - expr[0].kind == nkSym and - expr[0].sym.name.s == "==": - if expr[1].typ.isUnresolvedStatic: - inferConceptStaticParam(c, expr[1], expr[2]) - continue - elif expr[2].typ.isUnresolvedStatic: - inferConceptStaticParam(c, expr[2], expr[1]) - continue - - let verdict = semConstExpr(c, n[i]) - if verdict.intVal == 0: - localError(result.info, "concept predicate failed") - of tyUnknown: continue - else: discard - if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): - voidContext = true - n.typ = enforceVoidContext - if i == last and (length == 1 or efWantValue in flags): - n.typ = n.sons[i].typ - if not isEmptyType(n.typ): n.kind = nkStmtListExpr - elif i != last or voidContext: - discardCheck(c, n.sons[i]) - else: - n.typ = n.sons[i].typ - if not isEmptyType(n.typ): n.kind = nkStmtListExpr - 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: - for j in countup(i + 1, length - 1): - case n.sons[j].kind - of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, - nkBlockStmt, nkState: discard - else: localError(n.sons[j].info, errStmtInvalidAfterReturn) - else: discard + n.typ = n.sons[i].typ + if not isEmptyType(n.typ): n.kind = nkStmtListExpr + 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: + for j in countup(i + 1, length - 1): + case n.sons[j].kind + of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, + nkBlockStmt, nkState: discard + else: localError(n.sons[j].info, errStmtInvalidAfterReturn) + else: discard if result.len == 1 and # concept bodies should be preserved as a stmt list: @@ -1930,15 +1855,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = not (result.comment[0] == '#' and result.comment[1] == '#'): # it is an old-style comment statement: we replace it with 'discard ""': 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 = # now: simply an alias: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 1fc263617..537319d66 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1389,6 +1389,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = n.sons[2].kind == nkNilLit: result = freshType(result, prev) result.flags.incl(tfNotNil) + if notnil notin c.features: + localError(n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}") else: localError(n.info, errGenerated, "invalid type") of 2: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 96d815df7..4263ef581 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -26,6 +26,7 @@ type sym*: PSym unmatchedVarParam*, firstMismatch*: int diagnostics*: seq[string] + enabled*: bool CandidateErrors* = seq[CandidateError] @@ -60,7 +61,8 @@ type # matching. they will be reset if the matching # is not successful. may replace the bindings # 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 # displayed to the user. # triggered when overload resolution fails @@ -70,6 +72,7 @@ type inheritancePenalty: int # to prefer closest father object type firstMismatch*: int # position of the first type mismatch for # better error messages + diagnosticsEnabled*: bool TTypeRelFlag* = enum trDontBind @@ -124,7 +127,8 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = idTablePut(c.bindings, key, val.skipIntLit) 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) c.calleeSym = callee if callee.kind in skProcKinds and calleeScope == -1: @@ -139,7 +143,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, c.calleeScope = 1 else: c.calleeScope = calleeScope - c.diagnostics = if diagnostics: @[] else: nil + c.diagnostics = if diagnosticsEnabled: @[] else: nil + c.diagnosticsEnabled = diagnosticsEnabled c.magic = c.calleeSym.magic initIdTable(c.bindings) if binding != nil and callee.kind in routineKinds: @@ -717,7 +722,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = diagnostics: seq[string] errorPrefix: string flags: TExprFlags = {} - collectDiagnostics = m.diagnostics != nil or + collectDiagnostics = m.diagnosticsEnabled or sfExplain in typeClass.sym.flags if collectDiagnostics: @@ -736,7 +741,9 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = if collectDiagnostics: 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 @@ -1388,8 +1395,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, var aAsObject = roota.lastSon - if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind: - aAsObject = aAsObject.base + 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 if aAsObject.kind == tyObject: let baseType = aAsObject.base diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 4014d4c58..5413565e6 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -51,15 +51,15 @@ proc parseTopLevelStmt*(p: var TParsers): PNode = result = ast.emptyNode proc utf8Bom(s: string): int = - if s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF': + if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF': result = 3 else: result = 0 proc containsShebang(s: string, i: int): bool = - if s[i] == '#' and s[i+1] == '!': + if i+1 < s.len and s[i] == '#' and s[i+1] == '!': var j = i + 2 - while s[j] in Whitespace: inc(j) + while j < s.len and s[j] in Whitespace: inc(j) result = s[j] == '/' proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNode = @@ -74,9 +74,9 @@ proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNo discard llStreamReadLine(s, line) i = 0 inc linenumber - if line[i] == '#' and line[i+1] == '?': + if i+1 < line.len and line[i] == '#' and line[i+1] == '?': inc(i, 2) - while line[i] in Whitespace: inc(i) + while i < line.len and line[i] in Whitespace: inc(i) var q: TParser parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache) result = parser.parseAll(q) diff --git a/compiler/types.nim b/compiler/types.nim index 5d60fa9b4..edf4e5b46 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -612,13 +612,13 @@ proc firstOrd*(t: PType): BiggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic: result = firstOrd(lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(lastSon(t)) - else: internalError("invalid kind for first(" & $t.kind & ')') + else: internalError("invalid kind for firstOrd(" & $t.kind & ')') else: - internalError("invalid kind for first(" & $t.kind & ')') + internalError("invalid kind for firstOrd(" & $t.kind & ')') result = 0 proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = @@ -651,14 +651,14 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = of tyEnum: assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) result = t.n.sons[sonsLen(t.n) - 1].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic: result = lastOrd(lastSon(t)) of tyProxy: result = 0 of tyOrdinal: if t.len > 0: result = lastOrd(lastSon(t)) - else: internalError("invalid kind for last(" & $t.kind & ')') + else: internalError("invalid kind for lastOrd(" & $t.kind & ')') else: - internalError("invalid kind for last(" & $t.kind & ')') + internalError("invalid kind for lastOrd(" & $t.kind & ')') result = 0 proc lengthOrd*(t: PType): BiggestInt = diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 071cc7706..ba37237e8 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -208,7 +208,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add mapTypeToAst(t.sons[0], info) else: 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 tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c3eaf8946..0544dc311 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1553,6 +1553,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkFloatLit, info, t) of tyCString, tyString: result = newNodeIT(nkStrLit, info, t) + result.strVal = "" of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: result = newNodeIT(nkNilLit, info, t) |