diff options
108 files changed, 2260 insertions, 527 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 63b656715..6e651ed00 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -453,6 +453,7 @@ type nfIsRef # this node is a 'ref' node; used for the VM nfPreventCg # this node should be ignored by the codegen nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) + nfFromTemplate # a top-level node returned from a template TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) @@ -956,7 +957,8 @@ const skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, - nfIsRef, nfPreventCg, nfLL} + nfIsRef, nfPreventCg, nfLL, + nfFromTemplate} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 1fc8b7cea..e32fff181 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -219,7 +219,7 @@ proc rspaces(x: int): Rope = proc toYamlChar(c: char): string = case c - of '\0'..'\x1F', '\x80'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4) + of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4) of '\'', '\"', '\\': result = '\\' & c else: result = $c @@ -407,6 +407,8 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; var istr = rspaces(indent + 2) result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)] + when defined(useNodeIds): + addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)]) addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) if maxRecDepth != 0: addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)]) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f5b99c405..043064d98 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -545,7 +545,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "(($4)($1) * ($4)($2))", # MulF64 "(($4)($1) / ($4)($2))", # DivF64 - "($4)((NU$3)($1) >> (NU$3)($2))", # ShrI + "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI "($4)((NU$3)($1) << (NU$3)($2))", # ShlI "($4)($1 & $2)", # BitandI "($4)($1 | $2)", # BitorI @@ -585,16 +585,17 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "($1 != $2)"] # Xor var a, b: TLoc - s: BiggestInt + s, k: BiggestInt assert(e.sons[1].typ != nil) assert(e.sons[2].typ != nil) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) # BUGFIX: cannot use result-type here, as it may be a boolean s = max(getSize(a.t), getSize(b.t)) * 8 + k = getSize(a.t) * 8 putIntoDest(p, d, e.typ, binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), - getSimpleTypeDesc(p.module, e.typ)]) + getSimpleTypeDesc(p.module, e.typ), rope(k)]) proc genEqProc(p: BProc, e: PNode, d: var TLoc) = var a, b: TLoc @@ -664,7 +665,9 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = d.s = OnHeap else: var a: TLoc - let typ = skipTypes(e.sons[0].typ, abstractInst) + var typ = skipTypes(e.sons[0].typ, abstractInst) + if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass: + typ = typ.lastSon if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr: initLocExprSingleUse(p, e[0][0], d) return @@ -2148,8 +2151,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: - if (optDeadCodeElim notin gGlobalOptions and - sfDeadCodeElim notin getModule(prc).flags) or + if (not emitLazily(prc)) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index a094da783..378951d9d 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -39,13 +39,11 @@ proc genVarTuple(p: BProc, n: PNode) = var L = sonsLen(n) # if we have a something that's been captured, use the lowering instead: - var useLowering = false for i in countup(0, L-3): if n[i].kind != nkSym: - useLowering = true; break - if useLowering: - genStmts(p, lowerTupleUnpacking(n, p.prc)) - return + genStmts(p, lowerTupleUnpacking(n, p.prc)) + return + genLineDir(p, n) initLocExpr(p, n.sons[L-1], tup) var t = tup.t.skipTypes(abstractInst) @@ -182,10 +180,11 @@ proc genGotoVar(p: BProc; value: PNode) = lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) proc genSingleVar(p: BProc, a: PNode) = - var v = a.sons[0].sym - if {sfCompileTime, sfGoto} * v.flags != {}: + let v = a.sons[0].sym + if sfCompileTime in v.flags: return + if sfGoto in v.flags: # translate 'var state {.goto.} = X' into 'goto LX': - if sfGoto in v.flags: genGotoVar(p, a.sons[2]) + genGotoVar(p, a.sons[2]) return var targetProc = p if sfGlobal in v.flags: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 02eb5ba82..0c81ca814 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -470,7 +470,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode, let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype, check) if a != nil: - add(unionBody, "struct {") + if tfPacked notin rectype.flags: + add(unionBody, "struct {") + else: + addf(unionBody, CC[cCompiler].structStmtFmt, + [rope"struct", nil, rope(CC[cCompiler].packedPragma)]) + add(unionBody, "{") add(unionBody, a) addf(unionBody, "} $1;$n", [sname]) else: diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index c37a8fcdb..6a7aa8951 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -11,7 +11,7 @@ import ast, astalgo, ropes, hashes, strutils, types, msgs, wordrecg, - platform, trees + platform, trees, options proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = case n.kind @@ -211,20 +211,8 @@ proc mangle*(name: string): string = if requiresUnderscore: result.add "_" -proc makeLLVMString*(s: string): Rope = - const MaxLineLength = 64 - result = nil - var res = "c\"" - for i in countup(0, len(s) - 1): - if (i + 1) mod MaxLineLength == 0: - add(result, rope(res)) - setLen(res, 0) - case s[i] - of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': - add(res, '\\') - add(res, toHex(ord(s[i]), 2)) - else: add(res, s[i]) - add(res, "\\00\"") - add(result, rope(res)) +proc emitLazily*(s: PSym): bool {.inline.} = + result = optDeadCodeElim in gGlobalOptions or + sfDeadCodeElim in getModule(s).flags initTypeTables() diff --git a/compiler/cgen.nim b/compiler/cgen.nim index fab35c584..3797a92c2 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -47,10 +47,6 @@ proc findPendingModule(m: BModule, s: PSym): BModule = var ms = getModule(s) result = m.g.modules[ms.position] -proc emitLazily(s: PSym): bool {.inline.} = - result = optDeadCodeElim in gGlobalOptions or - sfDeadCodeElim in getModule(s).flags - proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) = result.k = k result.s = s @@ -142,6 +138,13 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope = template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped = ropecg(m, fmt, args) +var indent = "\t".rope + +proc indentLine(p: BProc, r: Rope): Rope = + result = r + for i in countup(0, p.blocks.len-1): + prepend(result, indent) + proc appcg(m: BModule, c: var Rope, frmt: FormatStr, args: varargs[Rope]) = add(c, ropecg(m, frmt, args)) @@ -154,11 +157,6 @@ proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr, args: varargs[Rope]) = add(p.s(s), ropecg(p.module, frmt, args)) -var indent = "\t".rope -proc indentLine(p: BProc, r: Rope): Rope = - result = r - for i in countup(0, p.blocks.len-1): prepend(result, indent) - proc line(p: BProc, s: TCProcSection, r: Rope) = add(p.s(s), indentLine(p, r)) @@ -603,8 +601,18 @@ proc generateHeaders(m: BModule) = addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)]) else: addf(m.s[cfsHeaders], "#include $1$N", [rope(it)]) + add(m.s[cfsHeaders], "#undef LANGUAGE_C" & tnl) + add(m.s[cfsHeaders], "#undef MIPSEB" & tnl) + add(m.s[cfsHeaders], "#undef MIPSEL" & tnl) + add(m.s[cfsHeaders], "#undef PPC" & tnl) + add(m.s[cfsHeaders], "#undef R3000" & tnl) + add(m.s[cfsHeaders], "#undef R4000" & tnl) + add(m.s[cfsHeaders], "#undef i386" & tnl) add(m.s[cfsHeaders], "#undef linux" & tnl) + add(m.s[cfsHeaders], "#undef mips" & tnl) add(m.s[cfsHeaders], "#undef near" & tnl) + add(m.s[cfsHeaders], "#undef powerpc" & tnl) + add(m.s[cfsHeaders], "#undef unix" & tnl) proc initFrame(p: BProc, procname, filename: Rope): Rope = discard cgsym(p.module, "nimFrame") diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index fda0b79dd..f088afcdb 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -155,5 +155,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode = #if ctx.instLines: result.info = n.info for i in countup(0, safeLen(body) - 1): evalTemplateAux(body.sons[i], args, ctx, result) + result.flags.incl nfFromTemplate result = wrapInComesFrom(n.info, result) dec(evalTemplateCounter) + diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ee35356c9..b31c3a6a7 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -94,12 +94,39 @@ type target: TTarget # duplicated here for faster dispatching unique: int # for temp identifier generation blocks: seq[TBlock] + extraIndent: int up: PProc # up the call chain; required for closure support declaredGlobals: IntSet template `|`(a, b: untyped): untyped {.dirty.} = (if p.target == targetJS: a else: b) +var indent = "\t".rope + +proc indentLine(p: PProc, r: Rope): Rope = + result = r + var p = p + while true: + for i in countup(0, p.blocks.len - 1 + p.extraIndent): + prepend(result, indent) + if p.up == nil or p.up.prc != p.prc.owner: + break + p = p.up + +template line(p: PProc, added: string) = + add(p.body, indentLine(p, rope(added))) + +template line(p: PProc, added: Rope) = + add(p.body, indentLine(p, added)) + +template lineF(p: PProc, frmt: FormatStr, args: varargs[Rope]) = + add(p.body, indentLine(p, ropes.`%`(frmt, args))) + +template nested(p, body) = + inc p.extraIndent + body + dec p.extraIndent + proc newGlobals(): PGlobals = new(result) result.forwarded = @[] @@ -129,7 +156,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode, module: module, procDef: procDef, g: globals, - target: module.target) + target: module.target, + extraIndent: int(procDef != nil)) if procDef != nil: result.prc = procDef.sons[namePos].sym if result.target == targetPHP: result.declaredGlobals = initIntSet() @@ -290,7 +318,7 @@ proc getTemp(p: PProc, defineInLocals: bool = true): Rope = if p.target == targetJS: result = "Tmp$1" % [rope(p.unique)] if defineInLocals: - addf(p.locals, "var $1;$n", [result]) + add(p.locals, p.indentLine("var $1;$n" % [result])) else: result = "$$Tmp$1" % [rope(p.unique)] @@ -315,9 +343,11 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = # tmp = b # tmp gen(p, a, x) - p.body.addf("if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc]) - gen(p, b, y) - p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc]) + lineF(p, "if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc]) + p.nested: + gen(p, b, y) + lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc]) + line(p, "}") proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone @@ -331,9 +361,11 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = r.res = p.getTemp r.kind = resVal gen(p, a, x) - p.body.addf("if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc]) - gen(p, b, y) - p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc]) + lineF(p, "if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc]) + p.nested: + gen(p, b, y) + lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc]) + line(p, "}") type TMagicFrmt = array[0..3, string] @@ -415,12 +447,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], - ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [ - "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", - "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", - "cstrToNimstr(($1)+\"\")", - "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", - "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], + ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], + ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], + ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], + ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] @@ -475,18 +505,18 @@ proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.res = frmt % [r.rdLoc] r.kind = resExpr -proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) = +proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes let i = ord(optOverflowCheck notin p.options) - useMagic(p, ops[op][i]) + useMagic(p, jsOps[op][i]) if sonsLen(n) > 2: gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc] + r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc] else: gen(p, n.sons[1], r) - r.res = ops[op][i + 2] % [r.rdLoc] + r.res = jsOps[op][i + 2] % [r.rdLoc] proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = case op @@ -501,7 +531,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = gen(p, n.sons[2], y) r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] else: - arithAux(p, n, r, op, jsOps) + arithAux(p, n, r, op) of mModI: if p.target == targetPHP: var x, y: TCompRes @@ -509,7 +539,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = gen(p, n.sons[2], y) r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc] else: - arithAux(p, n, r, op, jsOps) + arithAux(p, n, r, op) of mShrI: var x, y: TCompRes gen(p, n.sons[1], x) @@ -534,9 +564,9 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = else: gen(p, n.sons[1], r) else: - arithAux(p, n, r, op, jsOps) + arithAux(p, n, r, op) else: - arithAux(p, n, r, op, jsOps) + arithAux(p, n, r, op) r.kind = resExpr proc hasFrameInfo(p: PProc): bool = @@ -546,14 +576,14 @@ proc hasFrameInfo(p: PProc): bool = proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if optLineDir in p.options: - addf(p.body, "// line $2 \"$1\"$n", + lineF(p, "// line $2 \"$1\"$n", [rope(toFilename(n.info)), rope(line)]) if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and ((p.prc == nil) or sfPure notin p.prc.flags): useMagic(p, "endb") - addf(p.body, "endb($1);$n", [rope(line)]) + lineF(p, "endb($1);$n", [rope(line)]) elif hasFrameInfo(p): - addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) + lineF(p, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) proc genWhileStmt(p: PProc, n: PNode) = var @@ -566,20 +596,20 @@ proc genWhileStmt(p: PProc, n: PNode) = p.blocks[length].id = -p.unique p.blocks[length].isLoop = true let labl = p.unique.rope - addf(p.body, "L$1: while (true) {$n" | "while (true) {$n", [labl]) - gen(p, n.sons[0], cond) - addf(p.body, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n", + lineF(p, "L$1: while (true) {$n" | "while (true) {$n", [labl]) + p.nested: gen(p, n.sons[0], cond) + lineF(p, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n", [cond.res, labl]) - genStmt(p, n.sons[1]) - addf(p.body, "}$n" | "}L$#:;$n", [labl]) + p.nested: genStmt(p, n.sons[1]) + lineF(p, "}$n" | "}L$#:;$n", [labl]) setLen(p.blocks, length) proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = if src.kind != resNone: if dest.kind != resNone: - p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc]) + lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) else: - p.body.addf("$1;$n", [src.rdLoc]) + lineF(p, "$1;$n", [src.rdLoc]) src.kind = resNone src.res = nil @@ -620,8 +650,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = var tmpFramePtr = rope"F" if optStackTrace notin p.options: tmpFramePtr = p.getTemp(true) - add(p.body, tmpFramePtr & " = framePtr;" & tnl) - addf(p.body, "try {$n", []) + line(p, tmpFramePtr & " = framePtr;" & tnl) + lineF(p, "try {$n", []) if p.target == targetPHP and p.globals == nil: p.globals = "global $lastJSError; global $prevJSError;".rope var a: TCompRes @@ -632,18 +662,18 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if p.target == targetJS and catchBranchesExist: addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXC;$n --excHandler;$n", []) - add(p.body, "framePtr = $1;$n" % [tmpFramePtr]) + line(p, "framePtr = $1;$n" % [tmpFramePtr]) elif p.target == targetPHP: - addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", []) + lineF(p, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", []) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: # general except section: generalCatchBranchExists = true - if i > 1: addf(p.body, "else {$n", []) + if i > 1: lineF(p, "else {$n", []) gen(p, n.sons[i].sons[0], a) moveInto(p, a, r) - if i > 1: addf(p.body, "}$n", []) + if i > 1: lineF(p, "}$n", []) else: var orExpr: Rope = nil useMagic(p, "isObj") @@ -653,30 +683,32 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if orExpr != nil: add(orExpr, "||") addf(orExpr, "isObj($2lastJSError.m_type, $1)", [genTypeInfo(p, n.sons[i].sons[j].typ), dollar]) - if i > 1: add(p.body, "else ") - addf(p.body, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr]) + if i > 1: line(p, "else ") + lineF(p, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr]) gen(p, n.sons[i].sons[blen - 1], a) moveInto(p, a, r) - addf(p.body, "}$n", []) + lineF(p, "}$n", []) inc(i) if catchBranchesExist: if not generalCatchBranchExists: useMagic(p, "reraiseException") - add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl) + line(p, "else {" & tnl) + line(p, indent & "reraiseException();" & tnl) + line(p, "}" & tnl) addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) if p.target == targetJS: - add(p.body, "} finally {" & tnl) - add(p.body, "framePtr = $1;$n" % [tmpFramePtr]) + line(p, "} finally {" & tnl) + line(p, "framePtr = $1;$n" % [tmpFramePtr]) if p.target == targetPHP: # XXX ugly hack for PHP codegen - add(p.body, "}" & tnl) + line(p, "}" & tnl) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) if p.target == targetPHP: # XXX ugly hack for PHP codegen - add(p.body, "if($lastJSError) throw($lastJSError);" & tnl) + line(p, "if($lastJSError) throw($lastJSError);" & tnl) if p.target == targetJS: - add(p.body, "}" & tnl) + line(p, "}" & tnl) proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -685,11 +717,11 @@ proc genRaiseStmt(p: PProc, n: PNode) = gen(p, n.sons[0], a) let typ = skipTypes(n.sons[0].typ, abstractPtrs) useMagic(p, "raiseException") - addf(p.body, "raiseException($1, $2);$n", - [a.rdLoc, makeJSString(typ.sym.name.s)]) + lineF(p, "raiseException($1, $2);$n", + [a.rdLoc, makeJSString(typ.sym.name.s)]) else: useMagic(p, "reraiseException") - add(p.body, "reraiseException();" & tnl) + line(p, "reraiseException();" & tnl) proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var @@ -699,9 +731,9 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch and p.target == targetJS: useMagic(p, "toJSStr") - addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) + lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: - addf(p.body, "switch ($1) {$n", [cond.rdLoc]) + lineF(p, "switch ($1) {$n", [cond.rdLoc]) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) @@ -715,27 +747,29 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var v = copyNode(e.sons[0]) while v.intVal <= e.sons[1].intVal: gen(p, v, cond) - addf(p.body, "case $1: ", [cond.rdLoc]) + lineF(p, "case $1:$n", [cond.rdLoc]) inc(v.intVal) else: if stringSwitch: case e.kind - of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ", + of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n", [makeJSString(e.strVal, false)]) else: internalError(e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) - addf(p.body, "case $1: ", [cond.rdLoc]) - gen(p, lastSon(it), stmt) - moveInto(p, stmt, r) - addf(p.body, "$nbreak;$n", []) + lineF(p, "case $1:$n", [cond.rdLoc]) + p.nested: + gen(p, lastSon(it), stmt) + moveInto(p, stmt, r) + lineF(p, "break;$n", []) of nkElse: - addf(p.body, "default: $n", []) - gen(p, it.sons[0], stmt) - moveInto(p, stmt, r) - addf(p.body, "break;$n", []) + lineF(p, "default: $n", []) + p.nested: + gen(p, it.sons[0], stmt) + moveInto(p, stmt, r) + lineF(p, "break;$n", []) else: internalError(it.info, "jsgen.genCaseStmt") - addf(p.body, "}$n", []) + lineF(p, "}$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) @@ -746,13 +780,13 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = var sym = n.sons[0].sym sym.loc.k = locOther sym.position = idx+1 + let labl = p.unique + lineF(p, "L$1: do {$n" | "", [labl.rope]) setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet - let labl = p.unique - addf(p.body, "L$1: do {$n" | "", [labl.rope]) gen(p, n.sons[1], r) - addf(p.body, "} while(false);$n" | "$nL$#:;$n", [labl.rope]) setLen(p.blocks, idx) + lineF(p, "} while(false);$n" | "$nL$#:;$n", [labl.rope]) proc genBreakStmt(p: PProc, n: PNode) = var idx: int @@ -770,19 +804,23 @@ proc genBreakStmt(p: PProc, n: PNode) = if idx < 0 or not p.blocks[idx].isLoop: internalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - addf(p.body, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)]) + lineF(p, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)]) proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) + p.body.add p.indentLine(nil) for i in countup(0, sonsLen(n) - 1): case n.sons[i].kind - of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) + of nkStrLit..nkTripleStrLit: + p.body.add(n.sons[i].strVal) of nkSym: let v = n.sons[i].sym if p.target == targetPHP and v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: - add(p.body, "$") - add(p.body, mangleName(v, p.target)) - else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()") + p.body.add "$" + p.body.add mangleName(v, p.target) + else: + internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()") + p.body.add tnl proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes @@ -794,18 +832,18 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) = let it = n.sons[i] if sonsLen(it) != 1: if i > 0: - addf(p.body, "else {$n", []) + lineF(p, "else {$n", []) inc(toClose) - gen(p, it.sons[0], cond) - addf(p.body, "if ($1) {$n", [cond.rdLoc]) + p.nested: gen(p, it.sons[0], cond) + lineF(p, "if ($1) {$n", [cond.rdLoc]) gen(p, it.sons[1], stmt) else: # else part: - addf(p.body, "else {$n", []) - gen(p, it.sons[0], stmt) + lineF(p, "else {$n", []) + p.nested: gen(p, it.sons[0], stmt) moveInto(p, stmt, r) - addf(p.body, "}$n", []) - add(p.body, repeat('}', toClose) & tnl) + lineF(p, "}$n", []) + line(p, repeat('}', toClose) & tnl) proc generateHeader(p: PProc, typ: PType): Rope = result = nil @@ -854,7 +892,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = gen(p, x[0], a) gen(p, x[1], b) gen(p, y, c) - addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc]) + lineF(p, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc]) return var xtyp = mapType(p, x.typ) @@ -862,7 +900,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: gen(p, x.sons[0], a) let tmp = p.getTemp(false) - addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc]) + lineF(p, "var $1 = $2;$n", [tmp, a.rdLoc]) a.res = "$1[0][$1[1]]" % [tmp] else: gen(p, x, a) @@ -876,29 +914,29 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = case xtyp of etySeq: if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: - addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) + lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") - addf(p.body, "$1 = nimCopy(null, $2, $3);$n", - [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) + lineF(p, "$1 = nimCopy(null, $2, $3);$n", + [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: - addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) + lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") - addf(p.body, "nimCopy($1, $2, $3);$n", - [a.res, b.res, genTypeInfo(p, y.typ)]) + lineF(p, "nimCopy($1, $2, $3);$n", + [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if y.kind == nkCall: let tmp = p.getTemp(false) - addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) + lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) else: internalError(x.info, "genAsgn") else: - addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) + lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: - addf(p.body, "$1 = $2;$n", [a.res, b.res]) + lineF(p, "$1 = $2;$n", [a.res, b.res]) proc genAsgn(p: PProc, n: PNode) = genLineDir(p, n) @@ -906,7 +944,15 @@ proc genAsgn(p: PProc, n: PNode) = proc genFastAsgn(p: PProc, n: PNode) = genLineDir(p, n) - genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true) + # 'shallowCopy' always produced 'noCopyNeeded = true' here but this is wrong + # for code like + # while j >= pos: + # dest[i].shallowCopy(dest[j]) + # See bug #5933. So we try to be more compatible with the C backend semantics + # here for 'shallowCopy'. This is an educated guess and might require further + # changes later: + let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString} + genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy) proc genSwap(p: PProc, n: PNode) = var a, b: TCompRes @@ -917,12 +963,13 @@ proc genSwap(p: PProc, n: PNode) = let tmp2 = p.getTemp(false) if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") - addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | - "$1 = $2; $2 = $3; $3 = $1;$n", [ - tmp, a.address, b.address]) + lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n" | + "$1 = $2; $2 = $3; $3 = $1;$n", + [tmp, a.address, b.address]) tmp = tmp2 - addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" | - "$1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) + lineF(p, "var $1 = $2; $2 = $3; $3 = $1;" | + "$1 = $2; $2 = $3; $3 = $1;", + [tmp, a.res, b.res]) proc getFieldPosition(f: PNode): int = case f.kind @@ -1483,10 +1530,10 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = s: Rope if n.kind == nkEmpty: let mname = mangleName(v, p.target) - addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", - [mname, createVar(p, v.typ, isIndirect(v))]) + lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n", + [mname, createVar(p, v.typ, isIndirect(v))]) if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex: - addf(p.body, "var $1_Idx = 0;$n", [ mname ]) + lineF(p, "var $1_Idx = 0;$n", [ mname ]) else: discard mangleName(v, p.target) gen(p, n, a) @@ -1501,25 +1548,25 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {} if a.typ == etyBaseIndex: if targetBaseIndex: - addf(p.body, "var $1 = $2, $1_Idx = $3;$n", [ - v.loc.r, a.address, a.res]) + lineF(p, "var $1 = $2, $1_Idx = $3;$n", + [v.loc.r, a.address, a.res]) else: - addf(p.body, "var $1 = [$2, $3];$n", - [v.loc.r, a.address, a.res]) + lineF(p, "var $1 = [$2, $3];$n", + [v.loc.r, a.address, a.res]) else: if targetBaseIndex: let tmp = p.getTemp - addf(p.body, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n", - [tmp, a.res, v.loc.r]) + lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n", + [tmp, a.res, v.loc.r]) else: - addf(p.body, "var $1 = $2;$n", [v.loc.r, a.res]) + lineF(p, "var $1 = $2;$n", [v.loc.r, a.res]) return else: s = a.res if isIndirect(v): - addf(p.body, "var $1 = /**/[$2];$n", [v.loc.r, s]) + lineF(p, "var $1 = [$2];$n", [v.loc.r, s]) else: - addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s]) + lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s]) proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): @@ -1550,17 +1597,17 @@ proc genNew(p: PProc, n: PNode) = gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] if p.target == targetJS: - addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)]) + lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) else: - addf(p.body, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)]) + lineF(p, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - addf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" | - "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [ + lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" | + "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = @@ -1716,8 +1763,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = binaryExpr(p, n, r, "addChar", "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") else: - binaryExpr(p, n, r, "", - "$1 .= chr($2)") + binaryExpr(p, n, r, "", "$1 .= chr($2)") of mAppendStrStr: if p.target == targetJS: if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: @@ -1727,15 +1773,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") # XXX: make a copy of $2, because of Javascript's sucking semantics else: - binaryExpr(p, n, r, "", - "$1 .= $2;") + binaryExpr(p, n, r, "", "$1 .= $2;") of mAppendSeqElem: if p.target == targetJS: - binaryExpr(p, n, r, "", - "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }") + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + if needsNoCopy(p, n[2]): + r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc] + else: + useMagic(p, "nimCopy") + let c = getTemp(p, defineInLocals=false) + lineF(p, "var $1 = nimCopy(null, $2, $3);$n", + [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) + r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] + r.kind = resExpr else: - binaryExpr(p, n, r, "", - "$1[] = $2") + binaryExpr(p, n, r, "", "$1[] = $2") of mConStrStr: if p.target == targetJS: genConStrStr(p, n, r) @@ -1795,7 +1849,14 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)") - of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2" | "$1 = array_slice($1, 0, $2)") + of mSetLengthSeq: + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] + r.res = """if ($1.length < $2) { for (var i=$1.length;i<$2;++i) $1.push($3); } + else { $1.length = $2; }""" % [x.rdLoc, y.rdLoc, createVar(p, t, false)] + r.kind = resExpr of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)") @@ -1986,15 +2047,18 @@ proc genReturnStmt(p: PProc, n: PNode) = genStmt(p, n.sons[0]) else: genLineDir(p, n) - addf(p.body, "break BeforeRet;$n" | "goto BeforeRet;$n", []) + lineF(p, "break BeforeRet;$n" | "goto BeforeRet;$n", []) proc frameCreate(p: PProc; procname, filename: Rope): Rope = - result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$nframePtr = F;$n" | - "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n$$framePtr = &$$F;$n")) % [ - procname, filename] + let frameFmt = + "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" | + "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n" + + result = p.indentLine(frameFmt % [procname, filename]) + result.add p.indentLine(ropes.`%`("framePtr = F;$n" | "$$framePtr = &$$F;$n", [])) proc frameDestroy(p: PProc): Rope = - result = rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl) + result = p.indentLine rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl) proc genProcBody(p: PProc, prc: PSym): Rope = if hasFrameInfo(p): @@ -2004,8 +2068,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope = else: result = nil if p.beforeRetNeeded: - addf(result, "BeforeRet: do {$n$1} while (false); $n" | - "$# BeforeRet:;$n", [p.body]) + if p.target == targetJS: + result.add p.indentLine(~"BeforeRet: do {$n") + result.add p.body + result.add p.indentLine(~"} while (false);$n") + else: + addF(result, "$# BeforeRet:;$n", [p.body]) else: add(result, p.body) if prc.typ.callConv == ccSysCall and p.target == targetJS: @@ -2014,6 +2082,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope = if hasFrameInfo(p): add(result, frameDestroy(p)) +proc optionaLine(p: Rope): Rope = + if p == nil: + return nil + else: + return p & tnl + proc genProc(oldProc: PProc, prc: PSym): Rope = var resultSym: PSym @@ -2029,29 +2103,37 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym let mname = mangleName(resultSym, p.target) - resultAsgn = ("var $# = $#;$n" | "$$$# = $#;$n") % [ - mname, - createVar(p, resultSym.typ, isIndirect(resultSym))] + let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) + resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar]) if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex: - resultAsgn.add "var $#_Idx = 0;$n" % [ - mname ]; + resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) gen(p, prc.ast.sons[resultPos], a) if mapType(p, resultSym.typ) == etyBaseIndex: returnStmt = "return [$#, $#];$n" % [a.address, a.res] else: returnStmt = "return $#;$n" % [a.res] - genStmt(p, prc.getBody) - result = "function $#($#) {$n$#$n$#$#$#$#}$n" % - [name, header, p.globals, p.locals, resultAsgn, - genProcBody(p, prc), returnStmt] + p.nested: genStmt(p, prc.getBody) + let def = "function $#($#) {$n$#$#$#$#$#" % + [name, header, + optionaLine(p.globals), + optionaLine(p.locals), + optionaLine(resultAsgn), + optionaLine(genProcBody(p, prc)), + optionaLine(p.indentLine(returnStmt))] + + dec p.extraIndent + result = ~tnl + result.add p.indentLine(def) + result.add p.indentLine(~"}$n") + #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s proc genStmt(p: PProc, n: PNode) = var r: TCompRes gen(p, n, r) - if r.res != nil: addf(p.body, "$#;$n", [r.res]) + if r.res != nil: lineF(p, "$#;$n", [r.res]) proc genPragma(p: PProc, n: PNode) = for it in n.sons: @@ -2211,7 +2293,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkPragma: genPragma(p, n) of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym - if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: + if sfExportc in s.flags and compilingLib: genSym(p, n.sons[namePos], r) r.res = nil of nkGotoState, nkState: @@ -2342,3 +2424,4 @@ proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = result = r const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) + diff --git a/compiler/msgs.nim b/compiler/msgs.nim index e416a3826..5f4a0caf1 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -543,7 +543,7 @@ var proc toCChar*(c: char): string = case c - of '\0'..'\x1F', '\x80'..'\xFF': result = '\\' & toOctal(c) + of '\0'..'\x1F', '\x7F'..'\xFF': result = '\\' & toOctal(c) of '\'', '\"', '\\', '?': result = '\\' & c else: result = $(c) diff --git a/compiler/options.nim b/compiler/options.nim index c4a57f41c..5c39faf3b 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -186,6 +186,9 @@ var const oKeepVariableNames* = true +template compilingLib*: bool = + gGlobalOptions * {optGenGuiApp, optGenDynLib} != {} + proc mainCommandArg*: string = ## This is intended for commands like check or parse ## which will work on the main project file unless diff --git a/compiler/parser.nim b/compiler/parser.nim index d4c44788e..8bd99acd3 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -846,6 +846,7 @@ type TDeclaredIdentFlag = enum withPragma, # identifier may have pragma withBothOptional # both ':' and '=' parts are optional + withDot # allow 'var ident.ident = value' TDeclaredIdentFlags = set[TDeclaredIdentFlag] proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = @@ -859,7 +860,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = while true: case p.tok.tokType of tkSymbol, tkAccent: - if withPragma in flags: a = identWithPragma(p) + if withPragma in flags: a = identWithPragma(p, allowDot=withdot in flags) else: a = parseSymbol(p) if a.kind == nkEmpty: return else: break @@ -1889,7 +1890,7 @@ proc parseVarTuple(p: var TParser): PNode = optInd(p, result) # progress guaranteed while p.tok.tokType in {tkSymbol, tkAccent}: - var a = identWithPragma(p) + var a = identWithPragma(p, allowDot=true) addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) @@ -1905,7 +1906,7 @@ proc parseVariable(p: var TParser): PNode = #| colonBody = colcom stmt doBlocks? #| variable = (varTuple / identColonEquals) colonBody? indAndComment if p.tok.tokType == tkParLe: result = parseVarTuple(p) - else: result = parseIdentColonEquals(p, {withPragma}) + else: result = parseIdentColonEquals(p, {withPragma, withDot}) result{-1} = postExprBlocks(p, result{-1}) indAndComment(p, result) diff --git a/compiler/sem.nim b/compiler/sem.nim index 9f80e1399..cd0df0de0 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -442,6 +442,7 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode = result = nn proc semGenericStmt(c: PContext, n: PNode): PNode +proc semConceptBody(c: PContext, n: PNode): PNode include semtypes, semtempl, semgnrc, semstmts, semexprs diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 4fa4f7f32..5984e25e0 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -408,7 +408,13 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, else: # get rid of the deref again for a better error message: n.sons[1] = n.sons[1].sons[0] - notFoundError(c, n, errors) + #notFoundError(c, n, errors) + if efExplain notin flags: + # repeat the overload resolution, + # this time enabling all the diagnostic output (this should fail again) + discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) + else: + notFoundError(c, n, errors) else: if efExplain notin flags: # repeat the overload resolution, @@ -440,7 +446,8 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = assert n.kind == nkBracketExpr for i in 1..sonsLen(n)-1: - n.sons[i].typ = semTypeNode(c, n.sons[i], nil) + let e = semExpr(c, n.sons[i]) + n.sons[i].typ = e.typ.skipTypes({tyTypeDesc}) var s = s var a = n.sons[0] if a.kind == nkSym: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5da2b70fa..d422646a8 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -40,6 +40,11 @@ type bracketExpr*: PNode # current bracket expression (for ^ support) mapping*: TIdTable + TMatchedConcept* = object + candidateType*: PType + prev*: ptr TMatchedConcept + depth*: int + TInstantiationPair* = object genericSym*: PSym inst*: PInstantiation @@ -75,6 +80,7 @@ type importTable*: PScope # scope for all imported symbols topLevelScope*: PScope # scope for all top-level symbols p*: PProcCon # procedure context + matchedConcept*: ptr TMatchedConcept # the current concept being matched friendModules*: seq[PSym] # friend modules; may access private data; # this is used so that generic instantiations # can access private object fields @@ -82,7 +88,6 @@ type ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot # store this info in the syms themselves!) - inTypeClass*: int # > 0 if we are in a user-defined type class inGenericContext*: int # > 0 if we are in a generic type inUnrolledContext*: int # > 0 if we are unrolling a loop compilesContextId*: int # > 0 if we are in a ``compiles`` magic @@ -277,7 +282,7 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = assert n != nil result.n = n -proc newTypeWithSons2*(kind: TTypeKind, owner: PSym, sons: seq[PType]): PType = +proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]): PType = result = newType(kind, owner) result.sons = sons diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 59fa208d2..08a2e2ce9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -106,7 +106,10 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = result = convNotNeedeed return var d = skipTypes(castDest, abstractVar) - var s = skipTypes(src, abstractVar-{tyTypeDesc}) + var s = src + if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass: + s = s.lastSon + s = skipTypes(s, abstractVar-{tyTypeDesc}) var pointers = 0 while (d != nil) and (d.kind in {tyPtr, tyRef}) and (d.kind == s.kind): d = d.lastSon @@ -152,6 +155,8 @@ proc isCastable(dst, src: PType): bool = result = false elif typeAllowed(dst, skParam) != nil: result = false + elif dst.kind == tyProc and dst.callConv == ccClosure: + result = src.kind == tyProc and src.callConv == ccClosure else: result = (dstSize >= srcSize) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or @@ -227,7 +232,10 @@ proc semCast(c: PContext, n: PNode): PNode = if tfHasMeta in targetType.flags: localError(n.sons[0].info, errCastToANonConcreteType, $targetType) if not isCastable(targetType, castedExpr.typ): - localError(n.info, errExprCannotBeCastToX, $targetType) + let tar = $targetType + let alt = typeToString(targetType, preferDesc) + let msg = if tar != alt: tar & "=" & alt else: tar + localError(n.info, errExprCannotBeCastToX, msg) result = newNodeI(nkCast, n.info) result.typ = targetType addSon(result, copyTree(n.sons[0])) @@ -700,7 +708,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) - if c.inTypeClass == 0: + if c.matchedConcept == nil: result = evalAtCompileTime(c, result) proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = @@ -880,20 +888,6 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, if r.sym.name.id == field.id: result = r.sym else: illFormedAst(n) -proc makeDeref(n: PNode): PNode = - var t = skipTypes(n.typ, {tyGenericInst, tyAlias}) - result = n - if t.kind == tyVar: - result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) - addSon(result, n) - t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) - while t.kind in {tyPtr, tyRef}: - var a = result - let baseTyp = t.lastSon - result = newNodeIT(nkHiddenDeref, n.info, baseTyp) - addSon(result, a) - t = skipTypes(baseTyp, {tyGenericInst, tyAlias}) - const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias} @@ -920,7 +914,7 @@ proc readTypeParameter(c: PContext, typ: PType, else: discard - + if typ.kind != tyUserTypeClass: let ty = if typ.kind == tyCompositeTypeClass: typ.sons[1].skipGenericAlias else: typ.skipGenericAlias @@ -1138,9 +1132,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": ty = n.sons[0].typ return nil - ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias}) if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: ty = ty.lastSon + ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) var check: PNode = nil if ty.kind == tyObject: @@ -2156,7 +2150,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = let checks = if efNoEvaluateGeneric in flags: {checkUndeclared} else: {checkUndeclared, checkModule, checkAmbiguity} var s = qualifiedLookUp(c, n, checks) - if c.inTypeClass == 0: semCaptureSym(s, c.p.owner) + if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) result = semSym(c, n, s, flags) if s.kind in {skProc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) @@ -2285,14 +2279,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = pragma = n[1] pragmaName = considerQuotedIdent(pragma[0]) flags = flags - + case whichKeyword(pragmaName) of wExplain: flags.incl efExplain else: # what other pragmas are allowed for expressions? `likely`, `unlikely` invalidPragma(n) - + result = semExpr(c, n[0], flags) of nkPar: case checkPar(n) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 58239d23e..84cb0071f 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -594,8 +594,11 @@ proc foldConStrStr(m: PSym, n: PNode): PNode = proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode = result = newSymNode(s, info) - result.typ = newType(tyTypeDesc, s.owner) - result.typ.addSonSkipIntLit(s.typ) + if s.typ.kind != tyTypeDesc: + result.typ = newType(tyTypeDesc, s.owner) + result.typ.addSonSkipIntLit(s.typ) + else: + result.typ = s.typ proc getConstExpr(m: PSym, n: PNode): PNode = result = nil @@ -634,9 +637,11 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = newSymNodeTypeDesc(s, n.info) of skGenericParam: if s.typ.kind == tyStatic: - if s.typ.n != nil: + if s.typ.n != nil and tfUnresolved notin s.typ.flags: result = s.typ.n - result.typ = s.typ.sons[0] + result.typ = s.typ.base + elif s.typ.isIntLit: + result = s.typ.n else: result = newSymNodeTypeDesc(s, n.info) else: discard diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 3938259ad..7e55b266a 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -34,7 +34,7 @@ type type TSemGenericFlag = enum - withinBind, withinTypeDesc, withinMixin + withinBind, withinTypeDesc, withinMixin, withinConcept TSemGenericFlags = set[TSemGenericFlag] proc semGenericStmt(c: PContext, n: PNode, @@ -200,12 +200,13 @@ proc semGenericStmt(c: PContext, n: PNode, checkMinSonsLen(n, 1) let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) - if s == nil and withinMixin notin flags and + if s == nil and + {withinMixin, withinConcept}*flags == {} and fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx.toMixin: errorUndeclaredIdentifier(c, n.info, fn.renderTree) - var first = 0 + var first = ord(withinConcept in flags) var mixinContext = false if s != nil: incl(s.flags, sfUsed) @@ -471,3 +472,9 @@ proc semGenericStmt(c: PContext, n: PNode): PNode = ctx.toMixin = initIntset() result = semGenericStmt(c, n, {}, ctx) semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) + +proc semConceptBody(c: PContext, n: PNode): PNode = + var ctx: GenericCtx + ctx.toMixin = initIntset() + result = semGenericStmt(c, n, {withinConcept}, ctx) + semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b5dca4c1b..a28d322b1 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -88,7 +88,8 @@ proc sameInstantiation(a, b: TInstantiation): bool = if a.concreteTypes.len == b.concreteTypes.len: for i in 0..a.concreteTypes.high: if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], - flags = {ExactTypeDescValues}): return + flags = {ExactTypeDescValues, + ExactGcSafety}): return result = true proc genericCacheGet(genericSym: PSym, entry: TInstantiation; @@ -173,10 +174,14 @@ proc sideEffectsCheck(c: PContext, s: PSym) = proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = - var cl: TReplTypeVars + var + typeMap: LayeredIdTable + cl: TReplTypeVars + initIdTable(cl.symMap) - initIdTable(cl.typeMap) initIdTable(cl.localCache) + initIdTable(typeMap.topLayer) + cl.typeMap = addr(typeMap) cl.info = info cl.c = c cl.allowMetaTypes = allowMetaTypes @@ -200,7 +205,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, #addDecl(c, prc) pushInfoContext(info) - var cl = initTypeVars(c, pt, info, nil) + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(c, addr(typeMap), info, nil) var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy @@ -256,8 +262,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # NOTE: for access of private fields within generics from a different module # we set the friend module: c.friendModules.add(getModule(fn)) - let oldInTypeClass = c.inTypeClass - c.inTypeClass = 0 + let oldMatchedConcept = c.matchedConcept + c.matchedConcept = nil let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, false) @@ -318,5 +324,5 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, c.currentScope = oldScope discard c.friendModules.pop() dec(c.instCounter) - c.inTypeClass = oldInTypeClass + c.matchedConcept = oldMatchedConcept if result.kind == skMethod: finishMethod(c, result) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index eb6259df0..c664f735c 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -110,7 +110,7 @@ proc uninstantiate(t: PType): PType = else: t proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = - const skippedTypes = {tyTypeDesc} + const skippedTypes = {tyTypeDesc, tyAlias} let trait = traitCall[0] internalAssert trait.kind == nkSym var operand = operand.skipTypes(skippedTypes) @@ -119,7 +119,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = traitCall.sons[2].typ.skipTypes({tyTypeDesc}) template typeWithSonsResult(kind, sons): PNode = - newTypeWithSons2(kind, context, sons).toNode(traitCall.info) + newTypeWithSons(context, kind, sons).toNode(traitCall.info) case trait.sym.name.s of "or", "|": diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 96bdc6cba..c383f352f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -218,12 +218,11 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) = message(s.info, msgKind, "'$#' is not GC-safe as it calls '$#'" % [s.name.s, u.name.s]) - of skParam: + of skParam, skForVar: message(s.info, msgKind, "'$#' is not GC-safe as it performs an indirect call via '$#'" % [s.name.s, u.name.s]) else: - internalAssert u.kind == skUnknown message(u.info, msgKind, "'$#' is not GC-safe as it performs an indirect call here" % s.name.s) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 8f522ccc2..ee4203299 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -144,7 +144,7 @@ proc fixNilType(n: PNode) = n.typ = nil proc discardCheck(c: PContext, result: PNode) = - if c.inTypeClass > 0: return + if c.matchedConcept != nil: return if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if result.kind == nkNilLit: result.typ = nil @@ -465,6 +465,40 @@ proc hasEmpty(typ: PType): bool = for s in typ.sons: result = result or hasEmpty(s) +proc makeDeref(n: PNode): PNode = + var t = skipTypes(n.typ, {tyGenericInst, tyAlias}) + if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass: + t = t.lastSon + result = n + if t.kind == tyVar: + result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) + addSon(result, n) + t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) + while t.kind in {tyPtr, tyRef}: + var a = result + let baseTyp = t.lastSon + result = newNodeIT(nkHiddenDeref, n.info, baseTyp) + addSon(result, a) + t = skipTypes(baseTyp, {tyGenericInst, tyAlias}) + +proc fillPartialObject(c: PContext; n: PNode; typ: PType) = + if n.len == 2: + let x = semExprWithType(c, n[0]) + let y = considerQuotedIdent(n[1]) + let obj = x.typ.skipTypes(abstractPtrs) + if obj.kind == tyObject and tfPartial in obj.flags: + let field = newSym(skField, getIdent(y.s), obj.sym, n[1].info) + field.typ = skipIntLit(typ) + field.position = sonsLen(obj.n) + addSon(obj.n, newSymNode(field)) + n.sons[0] = makeDeref x + n.sons[1] = newSymNode(field) + else: + localError(n.info, "implicit object field construction " & + "requires a .partial object, but got " & typeToString(obj)) + else: + localError(n.info, "nkDotNode requires 2 children") + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) @@ -529,6 +563,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = message(a.info, warnEachIdentIsTuple) for j in countup(0, length-3): + if a[j].kind == nkDotExpr: + fillPartialObject(c, a[j], + if a.kind != nkVarTuple: typ else: tup.sons[j]) + addToVarSection(c, result, n, a) + continue var v = semIdentDef(c, a.sons[j], symkind) if sfGenSym notin v.flags and not isDiscardUnderscore(v): addInterfaceDecl(c, v) @@ -1092,7 +1131,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; x.add(it.sons[1]) x.add(prc) # recursion assures that this works for multiple macro annotations too: - result = semStmt(c, x) + result = semExpr(c, x) # since a proc annotation can set pragmas, we process these here again. # This is required for SqueakNim-like export pragmas. if result.kind in procDefs and result[namePos].kind == nkSym and @@ -1724,7 +1763,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = else: var expr = semExpr(c, n.sons[i], flags) n.sons[i] = expr - if c.inTypeClass > 0 and expr.typ != nil: + 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 @@ -1739,7 +1779,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = let verdict = semConstExpr(c, n[i]) if verdict.intVal == 0: - localError(result.info, "type class predicate failed") + localError(result.info, "concept predicate failed") of tyUnknown: continue else: discard if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): @@ -1763,7 +1803,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = if result.len == 1 and # concept bodies should be preserved as a stmt list: - c.inTypeClass == 0 and + c.matchedConcept == nil and # also, don't make life complicated for macros. # they will always expect a proper stmtlist: nfBlockArg notin n.flags and diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 09f90d8d0..0c2dd2f96 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -152,6 +152,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = message n[i].info, errGenerated, "region needs to be an object type" addSonSkipIntLit(result, region) addSonSkipIntLit(result, t) + if tfPartial in result.flags: + if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial) #if not isNilable: result.flags.incl tfNotNil proc semVarType(c: PContext, n: PNode, prev: PType): PType = @@ -1129,7 +1131,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = m.isNoCall = true matches(c, n, copyTree(n), m) - if m.state != csMatch and not m.typedescMatched: + if m.state != csMatch: let err = "cannot instantiate " & typeToString(t) & "\n" & "got: (" & describeArgs(c, n) & ")\n" & "but expected: (" & describeArgs(c, t.n, 0) & ")" @@ -1200,17 +1202,51 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = # if n.sonsLen == 0: return newConstraint(c, tyTypeClass) if nfBase2 in n.flags: message(n.info, warnDeprecated, "use 'concept' instead; 'generic'") - result = newOrPrevType(tyUserTypeClass, prev, c) - result.n = n - let pragmas = n[1] inherited = n[2] + result = newOrPrevType(tyUserTypeClass, prev, c) + var owner = getCurrOwner(c) + var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType]) + result.sons = @[candidateTypeSlot] + result.n = n + if inherited.kind != nkEmpty: for n in inherited.sons: let typ = semTypeNode(c, n, nil) - result.sons.safeAdd(typ) + result.sons.add(typ) + + openScope(c) + for param in n[0]: + var + dummyName: PNode + dummyType: PType + + let modifier = case param.kind + of nkVarTy: tyVar + of nkRefTy: tyRef + of nkPtrTy: tyPtr + of nkStaticTy: tyStatic + of nkTypeOfExpr: tyTypeDesc + else: tyNone + + if modifier != tyNone: + dummyName = param[0] + dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot) + if modifier == tyTypeDesc: dummyType.flags.incl tfExplicit + else: + dummyName = param + dummyType = candidateTypeSlot + + internalAssert dummyName.kind == nkIdent + var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar, + dummyName.ident, owner, owner.info) + dummyParam.typ = dummyType + addDecl(c, dummyParam) + + result.n.sons[3] = semConceptBody(c, n[3]) + closeScope(c) proc semProcTypeWithScope(c: PContext, n: PNode, prev: PType, kind: TSymKind): PType = @@ -1379,7 +1415,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = errorType(c) else: result = typeExpr.typ.base - if result.isMetaType: + if result.isMetaType and + result.kind != tyUserTypeClass: + # the dot expression may refer to a concept type in + # a different module. allow a normal alias then. let preprocessed = semGenericStmt(c, n) result = makeTypeFromExpr(c, preprocessed.copyTree) else: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 80fb9168b..b4a61deb7 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -73,9 +73,13 @@ proc cacheTypeInst*(inst: PType) = type + LayeredIdTable* = object + topLayer*: TIdTable + nextLayer*: ptr LayeredIdTable + TReplTypeVars* {.final.} = object c*: PContext - typeMap*: TIdTable # map PType to PType + typeMap*: ptr LayeredIdTable # map PType to PType symMap*: TIdTable # map PSym to PSym localCache*: TIdTable # local cache for remembering alraedy replaced # types during instantiation of meta types @@ -91,6 +95,23 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode +proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable = + copyIdTable(result.topLayer, pt) + +proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable = + result.nextLayer = cl.typeMap + initIdTable(result.topLayer) + +proc lookup(typeMap: ptr LayeredIdTable, key: PType): PType = + var tm = typeMap + while tm != nil: + result = PType(idTableGet(tm.topLayer, key)) + if result != nil: return + tm = tm.nextLayer + +template put(typeMap: ptr LayeredIdTable, key, value: PType) = + idTablePut(typeMap.topLayer, key, value) + template checkMetaInvariants(cl: TReplTypeVars, t: PType) = when false: if t != nil and tfHasMeta in t.flags and @@ -106,7 +127,8 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = let t = replaceTypeVarsT(cl, n.typ) if t != nil and t.kind == tyStatic and t.n != nil: - return t.n + return if tfUnresolved in t.flags: prepareNode(cl, t.n) + else: t.n result = copyNode(n) result.typ = t if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym) @@ -219,7 +241,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = result.ast = replaceTypeVarsN(cl, s.ast) proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType = - result = PType(idTableGet(cl.typeMap, t)) + result = cl.typeMap.lookup(t) if result == nil: if cl.allowMetaTypes or tfRetType in t.flags: return localError(t.sym.info, errCannotInstantiateX, typeToString(t)) @@ -227,7 +249,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType = # In order to prevent endless recursions, we must remember # this bad lookup and replace it with errorType everywhere. # These code paths are only active in "nim check" - idTablePut(cl.typeMap, t, result) + cl.typeMap.put(t, result) elif result.kind == tyGenericParam and not cl.allowMetaTypes: internalError(cl.info, "substitution with generic parameter") @@ -243,6 +265,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # tyGenericInvocation[A, tyGenericInvocation[A, B]] # is difficult to handle: + const eqFlags = eqTypeFlags + {tfGcSafe} var body = t.sons[0] if body.kind != tyGenericBody: internalError(cl.info, "no generic body") var header: PType = t @@ -252,7 +275,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = else: result = searchInstTypes(t) - if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return + if result != nil and eqFlags*result.flags == eqFlags*t.flags: return for i in countup(1, sonsLen(t) - 1): var x = t.sons[i] if x.kind in {tyGenericParam}: @@ -267,7 +290,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = if header != t: # search again after first pass: result = searchInstTypes(header) - if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return + if result != nil and eqFlags*result.flags == eqFlags*t.flags: return else: header = instCopyType(cl, t) @@ -285,12 +308,16 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = let oldSkipTypedesc = cl.skipTypedesc cl.skipTypedesc = true + + var typeMapLayer = newTypeMapLayer(cl) + cl.typeMap = addr(typeMapLayer) + for i in countup(1, sonsLen(t) - 1): var x = replaceTypeVarsT(cl, t.sons[i]) assert x.kind != tyGenericInvocation header.sons[i] = x propagateToOwner(header, x) - idTablePut(cl.typeMap, body.sons[i-1], x) + cl.typeMap.put(body.sons[i-1], x) for i in countup(1, sonsLen(t) - 1): # if one of the params is not concrete, we cannot do anything @@ -303,6 +330,9 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = cl.skipTypedesc = oldSkipTypedesc newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) result.flags = result.flags + newbody.flags - tfInstClearedFlags + + cl.typeMap = cl.typeMap.nextLayer + # This is actually wrong: tgeneric_closure fails with this line: #newbody.callConv = body.callConv # This type may be a generic alias and we want to resolve it here. @@ -404,7 +434,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = if t == nil: return if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses: - let lookup = PType(idTableGet(cl.typeMap, t)) + let lookup = cl.typeMap.lookup(t) if lookup != nil: return lookup case t.kind @@ -446,7 +476,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = skipIntLit(t) of tyTypeDesc: - let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t) + let lookup = cl.typeMap.lookup(t) if lookup != nil: result = lookup if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base @@ -485,7 +515,6 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = propagateToOwner(result, r) # bug #4677: Do not instantiate effect lists result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc)) - case result.kind of tyArray: let idx = result.sons[0] @@ -500,18 +529,19 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: discard -proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo; +proc initTypeVars*(p: PContext, typeMap: ptr LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = initIdTable(result.symMap) - copyIdTable(result.typeMap, pt) initIdTable(result.localCache) + result.typeMap = typeMap result.info = info result.c = p result.owner = owner proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; owner: PSym, allowMetaTypes = false): PNode = - var cl = initTypeVars(p, pt, n.info, owner) + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(p, addr(typeMap), n.info, owner) cl.allowMetaTypes = allowMetaTypes pushInfoContext(n.info) result = replaceTypeVarsN(cl, n) @@ -519,7 +549,8 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; original, new: PSym): PNode = - var cl = initTypeVars(p, pt, n.info, original) + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(p, addr(typeMap), n.info, original) idTablePut(cl.symMap, original, new) pushInfoContext(n.info) result = replaceTypeVarsN(cl, n) @@ -527,14 +558,16 @@ proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = - var cl = initTypeVars(p, pt, info, nil) + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(p, addr(typeMap), info, nil) pushInfoContext(info) result = replaceTypeVarsT(cl, t) popInfoContext() proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = - var cl = initTypeVars(p, pt, info, nil) + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(p, addr(typeMap), info, nil) cl.allowMetaTypes = true pushInfoContext(info) result = replaceTypeVarsT(cl, t) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index cb526947e..6084e11c0 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -204,7 +204,9 @@ proc sumGeneric(t: PType): int = if t.sons[i] != nil: result += t.sons[i].sumGeneric break - of tyGenericParam, tyExpr, tyStatic, tyStmt: break + of tyStatic: + return t.sons[0].sumGeneric + 1 + of tyGenericParam, tyExpr, tyStmt: break of tyAlias: t = t.lastSon of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, @@ -302,8 +304,32 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; add(result, argTypeToString(arg, prefer)) if i != sonsLen(n) - 1: add(result, ", ") -proc typeRel*(c: var TCandidate, f, aOrig: PType, - flags: TTypeRelFlags = {}): TTypeRelation +proc typeRelImpl*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation + +const traceTypeRel = false + +when traceTypeRel: + var nextTypeRel = 0 + +template typeRel*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation = + when traceTypeRel: + var enteringAt = nextTypeRel + if mdbg: + inc nextTypeRel + echo "----- TYPE REL ", enteringAt + debug f + debug aOrig + # writeStackTrace() + + let r = typeRelImpl(c, f, aOrig, flags) + + when traceTypeRel: + if enteringAt != nextTypeRel: + echo "----- TYPE REL ", enteringAt, " RESULT: ", r + + r proc concreteType(c: TCandidate, t: PType): PType = case t.kind @@ -602,19 +628,29 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = else: result = isNone -proc matchUserTypeClass*(c: PContext, m: var TCandidate, - ff, a: PType): PType = +proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = var + c = m.c typeClass = ff.skipTypes({tyUserTypeClassInst}) body = typeClass.n[3] - if c.inTypeClass > 4: - localError(body.info, $body & " too nested for type matching") - return nil + matchedConceptContext: TMatchedConcept + prevMatchedConcept = c.matchedConcept + prevCandidateType = typeClass[0][0] + + if prevMatchedConcept != nil: + matchedConceptContext.prev = prevMatchedConcept + matchedConceptContext.depth = prevMatchedConcept.depth + 1 + if prevMatchedConcept.depth > 4: + localError(body.info, $body & " too nested for type matching") + return nil openScope(c) - inc c.inTypeClass + matchedConceptContext.candidateType = a + typeClass[0].sons[0] = a + c.matchedConcept = addr(matchedConceptContext) defer: - dec c.inTypeClass + c.matchedConcept = prevMatchedConcept + typeClass[0].sons[0] = prevCandidateType closeScope(c) var typeParams: seq[(PSym, PType)] @@ -625,6 +661,9 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, typeParamName = ff.base.sons[i-1].sym.name typ = ff.sons[i] param: PSym + alreadyBound = PType(idTableGet(m.bindings, typ)) + + if alreadyBound != nil: typ = alreadyBound template paramSym(kind): untyped = newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info) @@ -658,33 +697,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, addDecl(c, param) - for param in typeClass.n[0]: - var - dummyName: PNode - dummyType: PType - - let modifier = case param.kind - of nkVarTy: tyVar - of nkRefTy: tyRef - of nkPtrTy: tyPtr - of nkStaticTy: tyStatic - of nkTypeOfExpr: tyTypeDesc - else: tyNone - - if modifier != tyNone: - dummyName = param[0] - dummyType = c.makeTypeWithModifier(modifier, a) - if modifier == tyTypeDesc: dummyType.flags.incl tfExplicit - else: - dummyName = param - dummyType = a - - internalAssert dummyName.kind == nkIdent - var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar, - dummyName.ident, typeClass.sym, typeClass.sym.info) - dummyParam.typ = dummyType - addDecl(c, dummyParam) - var oldWriteHook: type(writelnHook) diagnostics: seq[string] @@ -826,7 +838,7 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons) inferred.n = newIntNode(nkIntLit, rhs) put(c, lhs.typ, inferred) - if c.c.inTypeClass > 0: + if c.c.matchedConcept != nil: # inside concepts, binding is currently done with # direct mutation of the involved types: lhs.typ.n = inferred.n @@ -888,8 +900,8 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = else: return false -proc typeRel(c: var TCandidate, f, aOrig: PType, - flags: TTypeRelFlags = {}): TTypeRelation = +proc typeRelImpl(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation = # typeRel can be used to establish various relationships between types: # # 1) When used with concrete types, it will check for type equivalence @@ -916,7 +928,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, assert(aOrig != nil) var - useTypeLoweringRuleInTypeClass = c.c.inTypeClass > 0 and + useTypeLoweringRuleInTypeClass = c.c.matchedConcept != nil and not c.isNoCall and f.kind != tyTypeDesc and tfExplicit notin aOrig.flags @@ -965,7 +977,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # for example, but unfortunately `prepareOperand` is not called in certain # situation when nkDotExpr are rotated to nkDotCalls - if a.kind in {tyGenericInst, tyAlias} and + if aOrig.kind == tyAlias: + return typeRel(c, f, lastSon(aOrig)) + + if a.kind == tyGenericInst and skipTypes(f, {tyVar}).kind notin { tyGenericBody, tyGenericInvocation, tyGenericInst, tyGenericParam} + tyTypeClasses: @@ -1030,11 +1045,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: isNone of tyUserTypeClass, tyUserTypeClassInst: - # consider this: 'var g: Node' *within* a concept where 'Node' - # is a concept too (tgraph) - let x = typeRel(c, a, f, flags + {trDontBind}) - if x >= isGeneric: - return isGeneric + if c.c.matchedConcept != nil: + # consider this: 'var g: Node' *within* a concept where 'Node' + # is a concept too (tgraph) + let x = typeRel(c, a, f, flags + {trDontBind}) + if x >= isGeneric: + return isGeneric else: discard case f.kind @@ -1106,7 +1122,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if fRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, fRange, a) - elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic: + elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) else: if lengthOrd(fRange) != lengthOrd(aRange): @@ -1339,7 +1355,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNone else: result = typeRel(c, lastSon(f), a) - if result != isNone: put(c, f, a) + if result != isNone and a.kind != tyNil: + put(c, f, a) of tyGenericBody: considerPreviousT: @@ -1475,7 +1492,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = typeRel(c, f.lastSon, a) else: considerPreviousT: - var matched = matchUserTypeClass(c.c, c, f, aOrig) + if aOrig == f: return isEqual + var matched = matchUserTypeClass(c, f, aOrig) if matched != nil: bindConcreteTypeToUserTypeClass(matched, a) if doBind: put(c, f, matched) @@ -1567,6 +1585,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if not exprStructuralEquivalent(f.n, aOrig.n): result = isNone if result != isNone: put(c, f, aOrig) + elif aOrig.n != nil: + result = typeRel(c, f.lastSon, aOrig.n.typ) + if result != isNone: + var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) + boundType.n = aOrig.n + put(c, f, boundType) else: result = isNone elif prev.kind == tyStatic: @@ -1770,6 +1794,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, arg.typ.sons = @[evaluated.typ] arg.typ.n = evaluated a = arg.typ + else: + if m.callee.kind == tyGenericBody: + if f.kind == tyStatic and typeRel(m, f.base, a) != isNone: + result = makeStaticExpr(m.c, arg) + result.typ.flags.incl tfUnresolved + result.typ.n = arg + return var r = typeRel(m, f, a) @@ -2219,6 +2250,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = var def = copyTree(formal.ast) if def.kind == nkNilLit: def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c) + if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}: + put(m, formal.typ, def.typ) setSon(m.call, formal.position + 1, def) inc(f) # forget all inferred types if the overload matching failed diff --git a/compiler/trees.nim b/compiler/trees.nim index 8f0af89d3..c77dab349 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -41,6 +41,7 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool = of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal + of nkCommentStmt: result = a.comment == b.comment of nkEmpty, nkNilLit, nkType: result = true else: if sonsLen(a) == sonsLen(b): @@ -109,9 +110,11 @@ proc isDeepConstExpr*(n: PNode): bool = proc isRange*(n: PNode): bool {.inline.} = if n.kind in nkCallKinds: - if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or - n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and - n[0][1].sym.name.id == ord(wDotDot): + let callee = n[0] + if (callee.kind == nkIdent and callee.ident.id == ord(wDotDot)) or + (callee.kind == nkSym and callee.sym.name.id == ord(wDotDot)) or + (callee.kind in {nkClosedSymChoice, nkOpenSymChoice} and + callee[1].sym.name.id == ord(wDotDot)): result = true proc whichPragma*(n: PNode): TSpecialWord = diff --git a/compiler/types.nim b/compiler/types.nim index 2886ac619..dc7cd52db 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -139,6 +139,9 @@ proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string = add(result, ')') if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ, prefer)) + result.add "[declared in " + result.add($sym.info) + result.add "]" proc elemType*(t: PType): PType = assert(t != nil) @@ -421,6 +424,12 @@ template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) = tc.sons.safeAdd concrete tc.flags.incl tfResolved +# TODO: It would be a good idea to kill the special state of a resolved +# concept by switching to tyAlias within the instantiated procs. +# Currently, tyAlias is always skipped with lastSon, which means that +# we can store information about the matched concept in another position. +# Then builtInFieldAccess can be modified to properly read the derived +# consts and types stored within the concept. template isResolvedUserTypeClass*(t: PType): bool = tfResolved in t.flags @@ -437,6 +446,13 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = t.sym.name.s & " literal(" & $t.n.intVal & ")" elif prefer == preferName or t.sym.owner.isNil: result = t.sym.name.s + if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0: + result.add ": " + var first = true + for son in t.sons: + if not first: result.add " or " + result.add son.typeToString + first = false else: result = t.sym.owner.name.s & '.' & t.sym.name.s result.addTypeFlags(t) @@ -458,7 +474,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, ']') of tyTypeDesc: if t.sons[0].kind == tyNone: result = "typedesc" - else: result = "typedesc[" & typeToString(t.sons[0]) & "]" + else: result = "type " & typeToString(t.sons[0]) of tyStatic: internalAssert t.len > 0 if prefer == preferGenericArg and t.n != nil: @@ -688,6 +704,7 @@ type ExactTypeDescValues ExactGenericParams ExactConstraints + ExactGcSafety AllowCommonBase TTypeCmpFlags* = set[TTypeCmpFlag] @@ -976,6 +993,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = cycleCheck() if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n result = sameChildrenAux(a, b, c) and sameFlags(a, b) + if result and ExactGcSafety in c.flags: + result = a.flags * {tfThread} == b.flags * {tfThread} if result and a.kind == tyProc: result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n)) diff --git a/compiler/vm.nim b/compiler/vm.nim index e201e98dc..b8e6467b5 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1406,6 +1406,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if dest.kind in {nkStrLit..nkTripleStrLit} and regs[rb].kind in {rkNode}: dest.strVal = regs[rb].node.strVal + elif dest.kind == nkCommentStmt and regs[rb].kind in {rkNode}: + dest.comment = regs[rb].node.strVal else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") of opcNNewNimNode: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c7d9be48c..ba89f88d4 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -826,7 +826,23 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mSubF64: genBinaryABC(c, n, dest, opcSubFloat) of mMulF64: genBinaryABC(c, n, dest, opcMulFloat) of mDivF64: genBinaryABC(c, n, dest, opcDivFloat) - of mShrI: genBinaryABCnarrowU(c, n, dest, opcShrInt) + of mShrI: + # the idea here is to narrow type if needed before executing right shift + # inlined modified: genNarrowU(c, n, dest) + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + # uint is uint64 in the VM, we we only need to mask the result for + # other unsigned types: + let tmp = c.genx(n.sons[1]) + if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: + c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8)) + + # inlined modified: genBinaryABC(c, n, dest, opcShrInt) + let tmp2 = c.genx(n.sons[2]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABC(n, opcShrInt, dest, tmp, tmp2) + c.freeTemp(tmp) + c.freeTemp(tmp2) + of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt) of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt) of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt) diff --git a/doc/astspec.txt b/doc/astspec.txt index f430677af..57f6b9d8c 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -461,8 +461,8 @@ Documentation Comments ---------------------- Double-hash (``##``) comments in the code actually have their own format, -but the comments do not yet show up in the AST, which will only show that -a comment exists, not what it contains. Single-hash (``#``) comments are ignored. +using ``strVal`` to get and set the comment text. Single-hash (``#``) +comments are ignored. Concrete syntax: diff --git a/doc/nims.rst b/doc/nims.rst index 967dd4149..d4ef0055f 100644 --- a/doc/nims.rst +++ b/doc/nims.rst @@ -108,3 +108,12 @@ installation of Nimble is done with this simple script: mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe +You can also use the shebang ``#!/usr/bin/env nim``, as long as your filename +ends with ``.nims``: + +.. code-block:: nim + + #!/usr/bin/env nim + mode = ScriptMode.Silent + + echo "hello world" diff --git a/koch.nim b/koch.nim index 0fa06f273..aaa03d558 100644 --- a/koch.nim +++ b/koch.nim @@ -152,6 +152,10 @@ proc tryExec(cmd: string): bool = proc safeRemove(filename: string) = if existsFile(filename): removeFile(filename) +proc overwriteFile(source, dest: string) = + safeRemove(dest) + moveFile(source, dest) + proc copyExe(source, dest: string) = safeRemove(dest) copyFile(dest=dest, source=source) @@ -388,33 +392,17 @@ proc clean(args: string) = # -------------- builds a release --------------------------------------------- -proc patchConfig(lookFor, replaceBy: string) = - const - cfgFile = "config/nim.cfg" - try: - let cfg = readFile(cfgFile) - let newCfg = cfg.replace(lookFor, replaceBy) - if newCfg == cfg: - echo "Could not patch 'config/nim.cfg' [Error]" - echo "Reason: patch substring not found:" - echo lookFor - else: - writeFile(cfgFile, newCfg) - except IOError: - quit "Could not access 'config/nim.cfg' [Error]" - proc winReleaseArch(arch: string) = doAssert arch in ["32", "64"] let cpu = if arch == "32": "i386" else: "amd64" template withMingw(path, body) = - const orig = """#gcc.path = r"$nim\dist\mingw\bin"""" - let replacePattern = """gcc.path = r"..\mingw$1\bin" # winrelease""" % arch - patchConfig(orig, replacePattern) + let prevPath = getEnv("PATH") + putEnv("PATH", path & PathSep & prevPath) try: body finally: - patchConfig(replacePattern, orig) + putEnv("PATH", prevPath) withMingw r"..\mingw" & arch & r"\bin": # Rebuilding koch is necessary because it uses its pointer size to @@ -422,7 +410,7 @@ proc winReleaseArch(arch: string) = nimexec "c --out:koch_temp --cpu:$# koch" % cpu exec "koch_temp boot -d:release --cpu:$#" % cpu exec "koch_temp zip -d:release" - moveFile r"build\nim-$#.zip" % VersionAsString, + overwriteFile r"build\nim-$#.zip" % VersionAsString, r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch] proc winRelease() = @@ -431,8 +419,8 @@ proc winRelease() = web(gaCode) withDir "web/upload/" & VersionAsString: exec "7z a -tzip docs-$#.zip *.html" % VersionAsString - moveFile "web/upload/$1/docs-$1.zip" % VersionAsString, - "web/upload/download/docs-$1.zip" % VersionAsString + overwriteFile "web/upload/$1/docs-$1.zip" % VersionAsString, + "web/upload/download/docs-$1.zip" % VersionAsString when true: csource("-d:release") when true: @@ -482,6 +470,17 @@ proc temp(args: string) = copyExe(output, finalDest) if programArgs.len > 0: exec(finalDest & " " & programArgs) +proc xtemp(cmd: string) = + let d = getAppDir() + copyExe(d / "bin" / "nim".exe, d / "bin" / "nim_backup".exe) + try: + withDir(d): + temp"-d:debug" + copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe) + exec(cmd) + finally: + copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe) + proc pushCsources() = if not dirExists("../csources/.git"): quit "[Error] no csources git repository found" @@ -557,6 +556,7 @@ of cmdArgument: of "testinstall": testUnixInstall() of "test", "tests": tests(op.cmdLineRest) of "temp": temp(op.cmdLineRest) + of "xtemp": xtemp(op.cmdLineRest) of "winrelease": winRelease() of "wintools": bundleWinTools() of "nimble": buildNimble(existsDir(".git")) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 03ac09180..af1e9de28 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -255,6 +255,11 @@ proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} = result = newNimNode(nnkStrLit) result.strVal = s +proc newCommentStmtNode*(s: string): NimNode {.compileTime, noSideEffect.} = + ## creates a comment statement node + result = newNimNode(nnkCommentStmt) + result.strVal = s + proc newIntLitNode*(i: BiggestInt): NimNode {.compileTime.} = ## creates a int literal node from `i` result = newNimNode(nnkIntLit) @@ -275,6 +280,7 @@ proc newIdentNode*(i: string): NimNode {.compileTime.} = result = newNimNode(nnkIdent) result.ident = !i + type BindSymRule* = enum ## specifies how ``bindSym`` behaves brClosed, ## only the symbols in current scope are bound @@ -432,20 +438,105 @@ proc newLit*(c: char): NimNode {.compileTime.} = result = newNimNode(nnkCharLit) result.intVal = ord(c) -proc newLit*(i: BiggestInt): NimNode {.compileTime.} = + +proc newLit*(i: int): NimNode {.compileTime.} = ## produces a new integer literal node. result = newNimNode(nnkIntLit) result.intVal = i +proc newLit*(i: int8): NimNode {.compileTime.} = + ## produces a new integer literal node. + result = newNimNode(nnkInt8Lit) + result.intVal = i + +proc newLit*(i: int16): NimNode {.compileTime.} = + ## produces a new integer literal node. + result = newNimNode(nnkInt16Lit) + result.intVal = i + +proc newLit*(i: int32): NimNode {.compileTime.} = + ## produces a new integer literal node. + result = newNimNode(nnkInt32Lit) + result.intVal = i + +proc newLit*(i: int64): NimNode {.compileTime.} = + ## produces a new integer literal node. + result = newNimNode(nnkInt64Lit) + result.intVal = i + +proc newLit*(i: uint): NimNode {.compileTime.} = + ## produces a new unsigned integer literal node. + result = newNimNode(nnkUIntLit) + result.intVal = BiggestInt(i) + +proc newLit*(i: uint8): NimNode {.compileTime.} = + ## produces a new unsigned integer literal node. + result = newNimNode(nnkUInt8Lit) + result.intVal = BiggestInt(i) + +proc newLit*(i: uint16): NimNode {.compileTime.} = + ## produces a new unsigned integer literal node. + result = newNimNode(nnkUInt16Lit) + result.intVal = BiggestInt(i) + +proc newLit*(i: uint32): NimNode {.compileTime.} = + ## produces a new unsigned integer literal node. + result = newNimNode(nnkUInt32Lit) + result.intVal = BiggestInt(i) + +proc newLit*(i: uint64): NimNode {.compileTime.} = + ## produces a new unsigned integer literal node. + result = newNimNode(nnkUInt64Lit) + result.intVal = BiggestInt(i) + proc newLit*(b: bool): NimNode {.compileTime.} = ## produces a new boolean literal node. result = if b: bindSym"true" else: bindSym"false" -proc newLit*(f: BiggestFloat): NimNode {.compileTime.} = +when false: + # the float type is not really a distinct type as described in https://github.com/nim-lang/Nim/issues/5875 + proc newLit*(f: float): NimNode {.compileTime.} = + ## produces a new float literal node. + result = newNimNode(nnkFloatLit) + result.floatVal = f + +proc newLit*(f: float32): NimNode {.compileTime.} = ## produces a new float literal node. - result = newNimNode(nnkFloatLit) + result = newNimNode(nnkFloat32Lit) result.floatVal = f +proc newLit*(f: float64): NimNode {.compileTime.} = + ## produces a new float literal node. + result = newNimNode(nnkFloat64Lit) + result.floatVal = f + +when compiles(float128): + proc newLit*(f: float128): NimNode {.compileTime.} = + ## produces a new float literal node. + result = newNimNode(nnkFloat128Lit) + result.floatVal = f + +proc newLit*(arg: object): NimNode {.compileTime.} = + result = nnkObjConstr.newTree(arg.type.getTypeInst[1]) + for a, b in arg.fieldPairs: + result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) ) + +proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} = + result = nnkBracket.newTree + for x in arg: + result.add newLit(x) + +proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = + result = nnkBracket.newTree + for x in arg: + result.add newLit(x) + result = nnkPrefix.newTree(bindSym"@", result) + +proc newLit*(arg: tuple): NimNode {.compileTime.} = + result = nnkPar.newTree + for a,b in arg.fieldPairs: + result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) ) + proc newLit*(s: string): NimNode {.compileTime.} = ## produces a new string literal node. result = newNimNode(nnkStrLit) @@ -750,6 +841,8 @@ proc `$`*(node: NimNode): string {.compileTime.} = result = $node[0] of nnkAccQuoted: result = $node[0] + of nnkCommentStmt: + result = node.strVal else: badNodeKind node.kind, "$" diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 8d059dbbc..a374e80e8 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -127,7 +127,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = i.inc protocol.parseInt(result.minor, i) proc sendStatus(client: AsyncSocket, status: string): Future[void] = - client.send("HTTP/1.1 " & status & "\c\L") + client.send("HTTP/1.1 " & status & "\c\L\c\L") proc processClient(client: AsyncSocket, address: string, callback: proc (request: Request): diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index a104478ac..89b216b25 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -301,7 +301,7 @@ proc verifyReturnType(typeName: string) {.compileTime.} = proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = ## This macro transforms a single procedure into a closure iterator. ## The ``async`` macro supports a stmtList holding multiple async procedures. - if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef}: + if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: error("Cannot transform this node kind into an async proc." & " proc/method definition or lambda node expected.") diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 323af5a38..5b6701a12 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -590,8 +590,11 @@ proc enlarge[A, B](t: var OrderedTable[A, B]) = swap(t.data, n) while h >= 0: var nxt = n[h].next - if isFilled(n[h].hcode): - var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode) + let eh = n[h].hcode + if isFilled(eh): + var j: Hash = eh and maxHash(t) + while isFilled(t.data[j].hcode): + j = nextTry(j, maxHash(t)) rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j) h = nxt diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index c3390573a..603fee080 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -69,4 +69,4 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = result = affinitySpaceTotal().int else: result = sysconf(SC_NPROCESSORS_ONLN) - if result <= 0: result = 1 + if result <= 0: result = 0 diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index e8c8776c6..b015ed311 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -139,9 +139,14 @@ proc hash*(x: cstring): Hash = ## efficient hashing of null-terminated strings var h: Hash = 0 var i = 0 - while x[i] != 0.char: - h = h !& ord(x[i]) - inc i + when defined(js): + while i < x.len: + h = h !& ord(x[i]) + inc i + else: + while x[i] != 0.char: + h = h !& ord(x[i]) + inc i result = !$h proc hash*(sBuf: string, sPos, ePos: int): Hash = diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 4f43177a8..909a2613f 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -62,7 +62,8 @@ ## let body = %*{ ## "data": "some text" ## } -## echo client.request("http://some.api", httpMethod = HttpPost, body = $body) +## let response = client.request("http://some.api", httpMethod = HttpPost, body = $body) +## echo response.status ## ## Progress reporting ## ================== diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 6780ca164..e3d5191c6 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1001,7 +1001,7 @@ proc escapeJson*(s: string; result: var string) = result.add("\"") for x in runes(s): var r = int(x) - if r >= 32 and r <= 127: + if r >= 32 and r <= 126: var c = chr(r) case c of '"': result.add("\\\"") diff --git a/lib/pure/math.nim b/lib/pure/math.nim index a8432b6f0..8037b31b0 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -235,7 +235,7 @@ when not defined(JS): x = x and (not (1'u64 shl (64'u64-12'u64-e) - 1'u64)) result = cast[float64](x) - + proc truncImpl(f: float32): float32 = const mask : uint32 = 0xFF @@ -255,7 +255,7 @@ when not defined(JS): x = x and (not (1'u32 shl (32'u32-9'u32-e) - 1'u32)) result = cast[float32](x) - + proc trunc*(x: float64): float64 = if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x result = truncImpl(x) @@ -395,6 +395,12 @@ proc radToDeg*[T: float32|float64](d: T): T {.inline.} = ## Convert from radians to degrees result = T(d) / RadPerDeg +proc sgn*[T: SomeNumber](x: T): int {.inline.} = + ## Sign function. Returns -1 for negative numbers and `NegInf`, 1 for + ## positive numbers and `Inf`, and 0 for positive zero, negative zero and + ## `NaN`. + ord(T(0) < x) - ord(x < T(0)) + proc `mod`*[T: float32|float64](x, y: T): T = ## Computes the modulo operation for float operators. Equivalent ## to ``x - y * floor(x/y)``. Note that the remainder will always @@ -407,10 +413,13 @@ proc `mod`*[T: float32|float64](x, y: T): T = {.pop.} {.pop.} -proc `^`*[T](x, y: T): T = +proc `^`*[T](x: T, y: Natural): T = ## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use ## `pow <#pow,float,float>` for negative exponents. - assert y >= T(0) + when compiles(y >= T(0)): + assert y >= T(0) + else: + assert T(y) >= T(0) var (x, y) = (x, y) result = 1 @@ -447,6 +456,7 @@ when isMainModule and not defined(JS): assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 assert(erf(6.0) > erf(5.0)) assert(erfc(6.0) < erfc(5.0)) + when isMainModule: # Function for approximate comparison of floats proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) @@ -509,3 +519,21 @@ when isMainModule: doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf) doAssert(classify(trunc(f_nan.float32)) == fcNan) doAssert(classify(trunc(0.0'f32)) == fcZero) + + block: # sgn() tests + assert sgn(1'i8) == 1 + assert sgn(1'i16) == 1 + assert sgn(1'i32) == 1 + assert sgn(1'i64) == 1 + assert sgn(1'u8) == 1 + assert sgn(1'u16) == 1 + assert sgn(1'u32) == 1 + assert sgn(1'u64) == 1 + assert sgn(-12342.8844'f32) == -1 + assert sgn(123.9834'f64) == 1 + assert sgn(0'i32) == 0 + assert sgn(0'f32) == 0 + assert sgn(NegInf) == -1 + assert sgn(Inf) == 1 + assert sgn(NaN) == 0 + diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 307808556..5bdd3bc40 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -542,7 +542,6 @@ proc writeConfig*(dict: Config, filename: string) = let file = open(filename, fmWrite) defer: file.close() let fileStream = newFileStream(file) - defer: fileStream.close() dict.writeConfig(fileStream) proc getSectionValue*(dict: Config, section, key: string): string = diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 218f5ab81..23568edb9 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -149,26 +149,41 @@ proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} = ## retrieves the rest of the command line that has not been parsed yet. result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString +iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedString] = + ## This is an convenience iterator for iterating over the given OptParser object. + ## Example: + ## + ## .. code-block:: nim + ## var p = initOptParser("--left --debug:3 -l=4 -r:2") + ## for kind, key, val in p.getopt(): + ## case kind + ## of cmdArgument: + ## filename = key + ## of cmdLongOption, cmdShortOption: + ## case key + ## of "help", "h": writeHelp() + ## of "version", "v": writeVersion() + ## of cmdEnd: assert(false) # cannot happen + ## if filename == "": + ## # no filename has been given, so we show the help: + ## writeHelp() + p.pos = 0 + while true: + next(p) + if p.kind == cmdEnd: break + yield (p.kind, p.key, p.val) + when declared(initOptParser): iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] = - ## This is an convenience iterator for iterating over the command line. - ## This uses the OptParser object. Example: + ## This is an convenience iterator for iterating over the command line arguments. + ## This create a new OptParser object. + ## See above for a more detailed example ## ## .. code-block:: nim - ## var - ## filename = "" ## for kind, key, val in getopt(): - ## case kind - ## of cmdArgument: - ## filename = key - ## of cmdLongOption, cmdShortOption: - ## case key - ## of "help", "h": writeHelp() - ## of "version", "v": writeVersion() - ## of cmdEnd: assert(false) # cannot happen - ## if filename == "": - ## # no filename has been given, so we show the help: - ## writeHelp() + ## # this will iterate over all arguments passed to the cmdline. + ## continue + ## var p = initOptParser() while true: next(p) diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim index 7fd9c60fe..2e8dbe140 100644 --- a/lib/pure/parseopt2.nim +++ b/lib/pure/parseopt2.nim @@ -123,26 +123,41 @@ type {.deprecated: [TGetoptResult: GetoptResult].} +iterator getopt*(p: var OptParser): GetoptResult = + ## This is an convenience iterator for iterating over the given OptParser object. + ## Example: + ## + ## .. code-block:: nim + ## var p = initOptParser("--left --debug:3 -l=4 -r:2") + ## for kind, key, val in p.getopt(): + ## case kind + ## of cmdArgument: + ## filename = key + ## of cmdLongOption, cmdShortOption: + ## case key + ## of "help", "h": writeHelp() + ## of "version", "v": writeVersion() + ## of cmdEnd: assert(false) # cannot happen + ## if filename == "": + ## # no filename has been given, so we show the help: + ## writeHelp() + p.pos = 0 + while true: + next(p) + if p.kind == cmdEnd: break + yield (p.kind, p.key, p.val) + when declared(paramCount): iterator getopt*(): GetoptResult = - ## This is an convenience iterator for iterating over the command line. - ## This uses the OptParser object. Example: + ## This is an convenience iterator for iterating over the command line arguments. + ## This create a new OptParser object. + ## See above for a more detailed example ## ## .. code-block:: nim - ## var - ## filename = "" ## for kind, key, val in getopt(): - ## case kind - ## of cmdArgument: - ## filename = key - ## of cmdLongOption, cmdShortOption: - ## case key - ## of "help", "h": writeHelp() - ## of "version", "v": writeVersion() - ## of cmdEnd: assert(false) # cannot happen - ## if filename == "": - ## # no filename has been given, so we show the help: - ## writeHelp() + ## # this will iterate over all arguments passed to the cmdline. + ## continue + ## var p = initOptParser() while true: next(p) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 5c978a2f8..6a52e2cd5 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -371,7 +371,7 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string = of '\a': result = "\\a" of '\\': result = "\\\\" of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c - elif c < ' ' or c >= '\128': result = '\\' & $ord(c) + elif c < ' ' or c >= '\127': result = '\\' & $ord(c) elif c in reserved: result = '\\' & c else: result = $c diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 1f750edcd..8a32f7d9a 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -123,7 +123,8 @@ when not defined(nimscript): proc getMil(t: Time): int {.importcpp: "getTime", nodecl.} randomize(getMil times.getTime()) else: - randomize(int times.getTime()) + let time = int(times.epochTime() * 1_000_000_000) + randomize(time) {.pop.} diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index eea06f4ce..438b48beb 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -334,12 +334,16 @@ when not defined(js): if result > 0: copyMem(buffer, addr(s.data[s.pos]), result) inc(s.pos, result) + else: + result = 0 proc ssPeekData(s: Stream, buffer: pointer, bufLen: int): int = var s = StringStream(s) result = min(bufLen, s.data.len - s.pos) if result > 0: copyMem(buffer, addr(s.data[s.pos]), result) + else: + result = 0 proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) = var s = StringStream(s) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 458c22f3a..20b2657f6 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1643,7 +1643,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, ## * replaces any ``\`` by ``\\`` ## * replaces any ``'`` by ``\'`` ## * replaces any ``"`` by ``\"`` - ## * replaces any other character in the set ``{'\0'..'\31', '\128'..'\255'}`` + ## * replaces any other character in the set ``{'\0'..'\31', '\127'..'\255'}`` ## by ``\xHH`` where ``HH`` is its hexadecimal value. ## The procedure has been designed so that its output is usable for many ## different common syntaxes. The resulting string is prefixed with @@ -1653,7 +1653,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, result.add(prefix) for c in items(s): case c - of '\0'..'\31', '\128'..'\255': + of '\0'..'\31', '\127'..'\255': add(result, "\\x") add(result, toHex(ord(c), 2)) of '\\': add(result, "\\\\") diff --git a/lib/pure/times.nim b/lib/pure/times.nim index bad003a3e..eff912d12 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -81,7 +81,7 @@ when defined(posix) and not defined(JS): elif defined(windows): import winlean - when defined(vcc) or defined(bcc): + when defined(vcc) or defined(bcc) or defined(icl): # newest version of Visual C++ defines time_t to be of 64 bits type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64 # visual c's c runtime exposes these under a different name diff --git a/lib/system.nim b/lib/system.nim index fdc4c7db0..0e94bcc23 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1895,7 +1895,7 @@ const NimMinor*: int = 17 ## is the minor number of Nim's version. - NimPatch*: int = 0 + NimPatch*: int = 1 ## is the patch number of Nim's version. NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch @@ -2047,6 +2047,14 @@ proc clamp*[T](x, a, b: T): T = if x > b: return b return x +proc len*[T: Ordinal](x: Slice[T]): int {.noSideEffect, inline.} = + ## length of ordinal slice, when x.b < x.a returns zero length + ## + ## .. code-block:: Nim + ## assert((0..5).len == 6) + ## assert((5..2).len == 0) + result = max(0, ord(x.b) - ord(x.a) + 1) + iterator items*[T](a: openArray[T]): T {.inline.} = ## iterates over each item of `a`. var i = 0 @@ -2441,18 +2449,6 @@ when false: # ----------------- GC interface --------------------------------------------- when not defined(nimscript) and hasAlloc: - proc GC_disable*() {.rtl, inl, benign.} - ## disables the GC. If called n-times, n calls to `GC_enable` are needed to - ## reactivate the GC. Note that in most circumstances one should only disable - ## the mark and sweep phase with `GC_disableMarkAndSweep`. - - proc GC_enable*() {.rtl, inl, benign.} - ## enables the GC again. - - proc GC_fullCollect*() {.rtl, benign.} - ## forces a full garbage collection pass. - ## Ordinary code does not need to call this (and should not). - type GC_Strategy* = enum ## the strategy the GC should use for the application gcThroughput, ## optimize for throughput @@ -2462,33 +2458,87 @@ when not defined(nimscript) and hasAlloc: {.deprecated: [TGC_Strategy: GC_Strategy].} - proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.} - ## tells the GC the desired strategy for the application. - ## **Deprecated** since version 0.8.14. This has always been a nop. - - proc GC_enableMarkAndSweep*() {.rtl, benign.} - proc GC_disableMarkAndSweep*() {.rtl, benign.} - ## the current implementation uses a reference counting garbage collector - ## with a seldomly run mark and sweep phase to free cycles. The mark and - ## sweep phase may take a long time and is not needed if the application - ## does not create cycles. Thus the mark and sweep phase can be deactivated - ## and activated separately from the rest of the GC. - - proc GC_getStatistics*(): string {.rtl, benign.} - ## returns an informative string about the GC's activity. This may be useful - ## for tweaking. - - proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.} - proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.} - proc GC_ref*(x: string) {.magic: "GCref", benign.} - ## marks the object `x` as referenced, so that it will not be freed until - ## it is unmarked via `GC_unref`. If called n-times for the same object `x`, - ## n calls to `GC_unref` are needed to unmark `x`. - - proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.} - proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.} - proc GC_unref*(x: string) {.magic: "GCunref", benign.} - ## see the documentation of `GC_ref`. + when not defined(JS): + proc GC_disable*() {.rtl, inl, benign.} + ## disables the GC. If called n-times, n calls to `GC_enable` are needed to + ## reactivate the GC. Note that in most circumstances one should only disable + ## the mark and sweep phase with `GC_disableMarkAndSweep`. + + proc GC_enable*() {.rtl, inl, benign.} + ## enables the GC again. + + proc GC_fullCollect*() {.rtl, benign.} + ## forces a full garbage collection pass. + ## Ordinary code does not need to call this (and should not). + + proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.} + ## tells the GC the desired strategy for the application. + ## **Deprecated** since version 0.8.14. This has always been a nop. + + proc GC_enableMarkAndSweep*() {.rtl, benign.} + proc GC_disableMarkAndSweep*() {.rtl, benign.} + ## the current implementation uses a reference counting garbage collector + ## with a seldomly run mark and sweep phase to free cycles. The mark and + ## sweep phase may take a long time and is not needed if the application + ## does not create cycles. Thus the mark and sweep phase can be deactivated + ## and activated separately from the rest of the GC. + + proc GC_getStatistics*(): string {.rtl, benign.} + ## returns an informative string about the GC's activity. This may be useful + ## for tweaking. + + proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.} + proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.} + proc GC_ref*(x: string) {.magic: "GCref", benign.} + ## marks the object `x` as referenced, so that it will not be freed until + ## it is unmarked via `GC_unref`. If called n-times for the same object `x`, + ## n calls to `GC_unref` are needed to unmark `x`. + + proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.} + proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.} + proc GC_unref*(x: string) {.magic: "GCunref", benign.} + ## see the documentation of `GC_ref`. + + else: + template GC_disable* = + {.warning: "GC_disable is a no-op in JavaScript".} + + template GC_enable* = + {.warning: "GC_enable is a no-op in JavaScript".} + + template GC_fullCollect* = + {.warning: "GC_fullCollect is a no-op in JavaScript".} + + template GC_setStrategy* = + {.warning: "GC_setStrategy is a no-op in JavaScript".} + + template GC_enableMarkAndSweep* = + {.warning: "GC_enableMarkAndSweep is a no-op in JavaScript".} + + template GC_disableMarkAndSweep* = + {.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".} + + template GC_ref*[T](x: ref T) = + {.warning: "GC_ref is a no-op in JavaScript".} + + template GC_ref*[T](x: seq[T]) = + {.warning: "GC_ref is a no-op in JavaScript".} + + template GC_ref*(x: string) = + {.warning: "GC_ref is a no-op in JavaScript".} + + template GC_unref*[T](x: ref T) = + {.warning: "GC_unref is a no-op in JavaScript".} + + template GC_unref*[T](x: seq[T]) = + {.warning: "GC_unref is a no-op in JavaScript".} + + template GC_unref*(x: string) = + {.warning: "GC_unref is a no-op in JavaScript".} + + template GC_getStatistics*(): string = + {.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".} + "" template accumulateResult*(iter: untyped) = ## helps to convert an iterator to a proc. @@ -3223,16 +3273,6 @@ when not defined(JS): #and not defined(nimscript): elif defined(JS): # Stubs: - proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard - - proc GC_disable() = discard - proc GC_enable() = discard - proc GC_fullCollect() = discard - proc GC_setStrategy(strategy: GC_Strategy) = discard - proc GC_enableMarkAndSweep() = discard - proc GC_disableMarkAndSweep() = discard - proc GC_getStatistics(): string = return "" - proc getOccupiedMem(): int = return -1 proc getFreeMem(): int = return -1 proc getTotalMem(): int = return -1 @@ -3734,7 +3774,8 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = when hasAlloc and not defined(nimscript) and not defined(JS): proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} = - ## performs a deep copy of `x`. This is also used by the code generator + ## performs a deep copy of `y` and copies it into `x`. + ## This is also used by the code generator ## for the implementation of ``spawn``. discard diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index bcbc5d92f..78db96e77 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -9,7 +9,6 @@ # Low level allocator for Nim. Has been designed to support the GC. # TODO: -# - eliminate "used" field # - make searching for block O(1) {.push profiler:off.} diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index b0eb25616..cd03d2a54 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -25,6 +25,13 @@ when defined(nimTypeNames): c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", it.name, it.instances, it.sizes) it = it.nextType + when defined(nimGcRefLeak): + proc oomhandler() = + c_fprintf(stdout, "[Heap] ROOTS: #%ld\n", gch.additionalRoots.len) + writeLeaks() + + outOfMemHook = oomhandler + template decTypeSize(cell, t) = # XXX this needs to use atomics for multithreaded apps! when defined(nimTypeNames): diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 5896af88e..a97e974a1 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -142,11 +142,54 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes +when defined(nimGcRefLeak): + const + MaxTraceLen = 20 # tracking the last 20 calls is enough + + type + GcStackTrace = object + lines: array[0..MaxTraceLen-1, cstring] + files: array[0..MaxTraceLen-1, cstring] + + proc captureStackTrace(f: PFrame, st: var GcStackTrace) = + const + firstCalls = 5 + var + it = f + i = 0 + total = 0 + while it != nil and i <= high(st.lines)-(firstCalls-1): + # the (-1) is for the "..." entry + st.lines[i] = it.procname + st.files[i] = it.filename + inc(i) + inc(total) + it = it.prev + var b = it + while it != nil: + inc(total) + it = it.prev + for j in 1..total-i-(firstCalls-1): + if b != nil: b = b.prev + if total != i: + st.lines[i] = "..." + st.files[i] = "..." + inc(i) + while b != nil and i <= high(st.lines): + st.lines[i] = b.procname + st.files[i] = b.filename + inc(i) + b = b.prev + + var ax: array[10_000, GcStackTrace] + proc nimGCref(p: pointer) {.compilerProc.} = # we keep it from being collected by pretending it's not even allocated: when false: when withBitvectors: excl(gch.allocated, usrToCell(p)) else: usrToCell(p).refcount = rcBlack + when defined(nimGcRefLeak): + captureStackTrace(framePtr, ax[gch.additionalRoots.len]) add(gch.additionalRoots, usrToCell(p)) proc nimGCunref(p: pointer) {.compilerProc.} = @@ -157,6 +200,8 @@ proc nimGCunref(p: pointer) {.compilerProc.} = while i >= 0: if d[i] == cell: d[i] = d[L] + when defined(nimGcRefLeak): + ax[i] = ax[L] dec gch.additionalRoots.len break dec(i) @@ -164,6 +209,16 @@ proc nimGCunref(p: pointer) {.compilerProc.} = when withBitvectors: incl(gch.allocated, usrToCell(p)) else: usrToCell(p).refcount = rcWhite +when defined(nimGcRefLeak): + proc writeLeaks() = + for i in 0..gch.additionalRoots.len-1: + c_fprintf(stdout, "[Heap] NEW STACK TRACE\n") + for ii in 0..MaxTraceLen-1: + let line = ax[i].lines[ii] + let file = ax[i].files[ii] + if isNil(line): break + c_fprintf(stdout, "[Heap] %s(%s)\n", file, line) + include gc_common proc prepareDealloc(cell: PCell) = diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim index 3eda08df9..e7b9f65a7 100644 --- a/lib/system/gc_stack.nim +++ b/lib/system/gc_stack.nim @@ -79,7 +79,7 @@ template withRegion*(r: MemRegion; body: untyped) = try: body finally: - r = tlRegion + #r = tlRegion tlRegion = oldRegion template inc(p: pointer, s: int) = diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 431f84bfd..5b5ba9490 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -255,12 +255,21 @@ elif defined(gogc): next_gc: uint64 # next GC (in heap_alloc time) last_gc: uint64 # last GC (in absolute time) pause_total_ns: uint64 - pause_ns: array[256, uint64] + pause_ns: array[256, uint64] # circular buffer of recent gc pause lengths + pause_end: array[256, uint64] # circular buffer of recent gc end times (nanoseconds since 1970) numgc: uint32 + numforcedgc: uint32 # number of user-forced GCs + gc_cpu_fraction: float64 # fraction of CPU time used by GC enablegc: cbool debuggc: cbool # Statistics about allocation size classes. by_size: array[goNumSizeClasses, goMStats_inner_struct] + # Statistics below here are not exported to MemStats directly. + tinyallocs: uint64 # number of tiny allocations that didn't cause actual allocation; not exported to go directly + gc_trigger: uint64 + heap_live: uint64 + heap_scan: uint64 + heap_marked: uint64 proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl, importc: "runtime_ReadMemStats", diff --git a/lib/system/repr.nim b/lib/system/repr.nim index d9aa03b53..ab02c58a2 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -49,7 +49,7 @@ proc reprStrAux(result: var string, s: cstring; len: int) = of '"': add result, "\\\"" of '\\': add result, "\\\\" # BUGFIX: forgotten of '\10': add result, "\\10\"\n\"" # " \n " # better readability - of '\128' .. '\255', '\0'..'\9', '\11'..'\31': + of '\127' .. '\255', '\0'..'\9', '\11'..'\31': add result, "\\" & reprInt(ord(c)) else: result.add(c) @@ -68,7 +68,7 @@ proc reprChar(x: char): string {.compilerRtl.} = case x of '"': add result, "\\\"" of '\\': add result, "\\\\" - of '\128' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x)) + of '\127' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x)) else: add result, x add result, "\'" diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 6b0e32191..5c265a891 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -44,7 +44,7 @@ proc reprChar(x: char): string {.compilerRtl.} = case x of '"': add(result, "\\\"") of '\\': add(result, "\\\\") - of '\128'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) ) + of '\127'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) ) else: add(result, x) add(result, "\'") @@ -56,7 +56,7 @@ proc reprStrAux(result: var string, s: cstring, len: int) = of '"': add(result, "\\\"") of '\\': add(result, "\\\\") of '\10': add(result, "\\10\"\n\"") - of '\128'..'\255', '\0'..'\9', '\11'..'\31': + of '\127'..'\255', '\0'..'\9', '\11'..'\31': add( result, "\\" & reprInt(ord(c)) ) else: add( result, reprInt(ord(c)) ) # Not sure about this. diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index f61b887ad..6569f4f9f 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -117,6 +117,12 @@ else: when defined(linux) and defined(amd64): abi: array[48 div sizeof(clonglong), clonglong] + SysCondAttr {.importc: "pthread_condattr_t", pure, final + header: """#include <sys/types.h> + #include <pthread.h>""".} = object + when defined(linux) and defined(amd64): + abi: array[4 div sizeof(cint), cint] # actually a cint + SysLockType = distinct cint proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {. @@ -185,7 +191,7 @@ else: importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.} else: - proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {. + proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {. importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, importc: "pthread_cond_destroy", header: "<pthread.h>".} @@ -196,7 +202,7 @@ else: importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.} when defined(ios): - proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) = + proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = cond = cast[SysCond](c_malloc(sizeof(SysCondObj))) initSysCondAux(cond[], cond_attr) @@ -209,7 +215,7 @@ else: template signalSysCond(cond: var SysCond) = signalSysCondAux(cond[]) else: - template initSysCond(cond: var SysCond, cond_attr: pointer = nil) = + template initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = initSysCondAux(cond, cond_attr) template deinitSysCond(cond: var SysCond) = deinitSysCondAux(cond) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index d1012e9c5..49b13576c 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -264,7 +264,17 @@ else: proc getThreadId*(): int = result = int(lwp_gettid()) - elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): + elif defined(openbsd): + proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".} + + proc getThreadId*(): int = + result = int(getthrid()) + elif defined(netbsd): + proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".} + + proc getThreadId*(): int = + result = int(lwp_self()) + elif defined(macosx) or defined(freebsd): proc pthread_threadid_np(y: pointer; x: var uint64): cint {.importc, header: "pthread.h".} proc getThreadId*(): int = diff --git a/readme.md b/readme.md index 30cc14079..5e50bbc41 100644 --- a/readme.md +++ b/readme.md @@ -96,9 +96,8 @@ started contributing, you should familiarize yourself with the repository struct * ``bin/``, ``build/`` - these directories are empty, but are used when Nim is built. * ``compiler/`` - the compiler source code. Also includes nimfix, and plugins within - ``compiler/nimfix`` and ``compiler/plugins`` respectively. Nimsuggest was moved to - the [``nim-lang/nimsuggest``][nimsuggest-repo] repository, though it previously also - lived within the ``compiler/`` directory. + ``compiler/nimfix`` and ``compiler/plugins`` respectively. +* ``nimsuggest`` - the nimsuggest tool that previously lived in the [``nim-lang/nimsuggest``][nimsuggest-repo] repository. * ``config/`` - the configuration for the compiler and documentation generator. * ``doc/`` - the documentation files in reStructuredText format. * ``lib/`` - the standard library, including: diff --git a/tests/arithm/tshr.nim b/tests/arithm/tshr.nim new file mode 100644 index 000000000..e9b72f1df --- /dev/null +++ b/tests/arithm/tshr.nim @@ -0,0 +1,20 @@ +discard """ + output: '''''' +""" + +proc T() = + let VI = -8 + let VI64 = -8'i64 + let VI32 = -8'i32 + let VI16 = -8'i16 + let VI8 = -8'i8 + doAssert( (VI shr 1) == 9223372036854775804) + doAssert( (VI64 shr 1) == 9223372036854775804) + doAssert( (VI32 shr 1) == 2147483644) + doAssert( (VI16 shr 1) == 32764) + doAssert( (VI8 shr 1) == 124) + + +T() +static: + T() diff --git a/tests/async/tlambda.nim b/tests/async/tlambda.nim index e0ff1f483..d187c0d50 100644 --- a/tests/async/tlambda.nim +++ b/tests/async/tlambda.nim @@ -51,5 +51,8 @@ proc main() = var builder = newBuilder() + # Test {.async.} pragma with do notation: #5995 + builder.client = newClient("builder") do(client: Client, msg: JsonNode) {.async.}: + await onMessage(builder, msg) main() diff --git a/tests/bind/tinvalidbindtypedesc.nim b/tests/bind/tinvalidbindtypedesc.nim index 5b2f51110..7704d2cb7 100644 --- a/tests/bind/tinvalidbindtypedesc.nim +++ b/tests/bind/tinvalidbindtypedesc.nim @@ -1,6 +1,6 @@ discard """ line: 10 - errormsg: "type mismatch: got (typedesc[float], string)" + errormsg: "type mismatch: got (type float, string)" """ proc foo(T: typedesc; some: T) = diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim index bd00188fa..1431720e0 100644 --- a/tests/bind/tnicerrorforsymchoice.nim +++ b/tests/bind/tnicerrorforsymchoice.nim @@ -1,6 +1,6 @@ discard """ line: 18 - errormsg: "type mismatch: got (proc (s: TScgi) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}" + errormsg: "type mismatch: got (proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}" """ #bug #442 diff --git a/tests/concepts/t3414.nim b/tests/concepts/t3414.nim new file mode 100644 index 000000000..d45973034 --- /dev/null +++ b/tests/concepts/t3414.nim @@ -0,0 +1,22 @@ +type + View[T] = concept v + v.empty is bool + v.front is T + popFront v + +proc find(view: View; target: View.T): View = + result = view + + while not result.empty: + if view.front == target: + return + + mixin popFront + popFront result + +proc popFront[T](s: var seq[T]) = discard +proc empty[T](s: seq[T]): bool = false + +var s1 = @[1, 2, 3] +let s2 = s1.find(10) + diff --git a/tests/concepts/t4982.nim b/tests/concepts/t4982.nim new file mode 100644 index 000000000..9d82c83c9 --- /dev/null +++ b/tests/concepts/t4982.nim @@ -0,0 +1,18 @@ +discard """ +errormsg: "undeclared identifier: 'x'" +line: 10 +""" + +import typetraits # without this import the program compiles (and echos false) + +type + SomeTestConcept = concept t + x.name is string # typo: t.name was intended (which would result in echo true) + +type + TestClass = ref object of RootObj + name: string + +var test = TestClass(name: "mytest") +echo $(test is SomeTestConcept) + diff --git a/tests/concepts/t5888.nim b/tests/concepts/t5888.nim new file mode 100644 index 000000000..dbbab8c4c --- /dev/null +++ b/tests/concepts/t5888.nim @@ -0,0 +1,26 @@ +discard """ +output: ''' +true +true +true +f +0 +''' +""" + +import t5888lib/ca, t5888lib/opt + +type LocalCA = ca.CA + +proc f(c: CA) = + echo "f" + echo c.x + +var o = new(Opt) + +echo o is CA +echo o is LocalCA +echo o is ca.CA + +o.f() + diff --git a/tests/concepts/t5888lib/ca.nim b/tests/concepts/t5888lib/ca.nim new file mode 100644 index 000000000..4a811f797 --- /dev/null +++ b/tests/concepts/t5888lib/ca.nim @@ -0,0 +1,4 @@ +type + CA* = concept c + c.x is int + diff --git a/tests/concepts/t5888lib/opt.nim b/tests/concepts/t5888lib/opt.nim new file mode 100644 index 000000000..65d16addc --- /dev/null +++ b/tests/concepts/t5888lib/opt.nim @@ -0,0 +1,6 @@ +import ca + +type + Opt* = object + x*: int + diff --git a/tests/concepts/t5968.nim b/tests/concepts/t5968.nim new file mode 100644 index 000000000..adb374c65 --- /dev/null +++ b/tests/concepts/t5968.nim @@ -0,0 +1,20 @@ +discard """ + exitcode: 0 +""" + +type + Enumerable[T] = concept e + for it in e: + it is T + +proc cmap[T, G](e: Enumerable[T], fn: proc(t: T): G): seq[G] = + result = @[] + for it in e: result.add(fn(it)) + +import json + +var x = %["hello", "world"] + +var z = x.cmap(proc(it: JsonNode): string = it.getStr & "!") +assert z == @["hello!", "world!"] + diff --git a/tests/concepts/t5983.nim b/tests/concepts/t5983.nim new file mode 100644 index 000000000..e69647448 --- /dev/null +++ b/tests/concepts/t5983.nim @@ -0,0 +1,22 @@ +discard """ + output: "20.0 USD" +""" + +import typetraits + +const currencies = ["USD", "EUR"] # in real code 120 currencies + +type USD* = distinct float # in real code 120 types generates using macro +type EUR* = distinct float + +type CurrencyAmount = concept c + type t = c.type + const name = c.type.name + name in currencies + +proc `$`(x: CurrencyAmount): string = + $float(x) & " " & x.name + +let amount = 20.USD +echo amount + diff --git a/tests/concepts/templatesinconcepts.nim b/tests/concepts/templatesinconcepts.nim new file mode 100644 index 000000000..292b97ea6 --- /dev/null +++ b/tests/concepts/templatesinconcepts.nim @@ -0,0 +1,56 @@ +import typetraits + +template typeLen(x): int = x.type.name.len + +template bunchOfChecks(x) = + x.typeLen > 3 + x != 10 is bool + +template stmtListExprTmpl(x: untyped): untyped = + x is int + x + +type + Obj = object + x: int + + Gen[T] = object + x: T + + Eq = concept x, y + (x == y) is bool + + NotEq = concept x, y + (x != y) is bool + + ConceptUsingTemplate1 = concept x + echo x + sizeof(x) is int + bunchOfChecks x + + ConceptUsingTemplate2 = concept x + stmtListExprTmpl x + +template ok(x) = + static: assert(x) + +template no(x) = + static: assert(not(x)) + +ok int is Eq +ok int is NotEq +ok string is Eq +ok string is NotEq +ok Obj is Eq +ok Obj is NotEq +ok Gen[string] is Eq +ok Gen[int] is NotEq + +no int is ConceptUsingTemplate1 +ok float is ConceptUsingTemplate1 +no string is ConceptUsingTemplate1 + +ok int is ConceptUsingTemplate2 +no float is ConceptUsingTemplate2 +no string is ConceptUsingTemplate2 + diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim index 25a075fd1..417d1e502 100644 --- a/tests/concepts/texplain.nim +++ b/tests/concepts/texplain.nim @@ -9,33 +9,33 @@ proc e(o: ExplainedConcept): int texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo' texplain.nim(65, 6) ExplainedConcept: undeclared field: '.' texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(65, 5) ExplainedConcept: type class predicate failed +texplain.nim(65, 5) ExplainedConcept: concept predicate failed texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar' texplain.nim(66, 6) ExplainedConcept: undeclared field: '.' texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(65, 5) ExplainedConcept: type class predicate failed +texplain.nim(65, 5) ExplainedConcept: concept predicate failed texplain.nim(105, 10) Hint: Non-matching candidates for e(10) proc e(o: ExplainedConcept): int texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo' texplain.nim(65, 6) ExplainedConcept: undeclared field: '.' texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(65, 5) ExplainedConcept: type class predicate failed +texplain.nim(65, 5) ExplainedConcept: concept predicate failed texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar' texplain.nim(66, 6) ExplainedConcept: undeclared field: '.' texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(65, 5) ExplainedConcept: type class predicate failed +texplain.nim(65, 5) ExplainedConcept: concept predicate failed texplain.nim(109, 20) Error: type mismatch: got (NonMatchingType) but expected one of: proc e(o: ExplainedConcept): int -texplain.nim(65, 5) ExplainedConcept: type class predicate failed +texplain.nim(65, 5) ExplainedConcept: concept predicate failed proc e(i: int): int texplain.nim(110, 20) Error: type mismatch: got (NonMatchingType) but expected one of: proc r(o: RegularConcept): int -texplain.nim(69, 5) RegularConcept: type class predicate failed +texplain.nim(69, 5) RegularConcept: concept predicate failed proc r[T](a: SomeNumber; b: T; c: auto) proc r(i: string): int @@ -49,12 +49,12 @@ proc f(o: NestedConcept) texplain.nim(69, 6) RegularConcept: undeclared field: 'foo' texplain.nim(69, 6) RegularConcept: undeclared field: '.' texplain.nim(69, 6) RegularConcept: expression '.' cannot be called -texplain.nim(69, 5) RegularConcept: type class predicate failed +texplain.nim(69, 5) RegularConcept: concept predicate failed texplain.nim(70, 6) RegularConcept: undeclared field: 'bar' texplain.nim(70, 6) RegularConcept: undeclared field: '.' texplain.nim(70, 6) RegularConcept: expression '.' cannot be called -texplain.nim(69, 5) RegularConcept: type class predicate failed -texplain.nim(73, 5) NestedConcept: type class predicate failed +texplain.nim(69, 5) RegularConcept: concept predicate failed +texplain.nim(73, 5) NestedConcept: concept predicate failed ''' line: 119 errormsg: "type mismatch: got (MatchingType)" diff --git a/tests/concepts/tgraph.nim b/tests/concepts/tgraph.nim index a0177a043..985f04a61 100644 --- a/tests/concepts/tgraph.nim +++ b/tests/concepts/tgraph.nim @@ -1,29 +1,34 @@ -discard """ - output: '''XY is Node -MyGraph is Graph''' -""" # bug #3452 import math type - Node* = concept n - `==`(n, n) is bool + Node* = concept n + `==`(n, n) is bool - Graph* = concept g - var x: Node - distance(g, x, x) is float + Graph1* = concept g + type N = Node + distance(g, N, N) is float - XY* = tuple[x, y: int] + Graph2 = concept g + distance(g, Node, Node) is float - MyGraph* = object - points: seq[XY] + Graph3 = concept g + var x: Node + distance(g, x, x) is float -if XY is Node: - echo "XY is Node" + XY* = tuple[x, y: int] + + MyGraph* = object + points: seq[XY] + +static: + assert XY is Node proc distance*( g: MyGraph, a, b: XY): float = - sqrt( pow(float(a.x - b.x), 2) + pow(float(a.y - b.y), 2) ) + sqrt( pow(float(a.x - b.x), 2) + pow(float(a.y - b.y), 2) ) -if MyGraph is Graph: - echo "MyGraph is Graph" +static: + assert MyGraph is Graph1 + assert MyGraph is Graph2 + assert MyGraph is Graph3 diff --git a/tests/concepts/trandomvars.nim b/tests/concepts/trandomvars.nim new file mode 100644 index 000000000..db41aa901 --- /dev/null +++ b/tests/concepts/trandomvars.nim @@ -0,0 +1,61 @@ +discard """ +output: ''' +true +true +true +3 +18.0 +324.0 +''' +""" + +type RNG = object + +proc random(rng: var RNG): float = 1.0 + +type + RandomVar[A] = concept x + var rng: RNG + rng.sample(x) is A + + Constant[A] = object + value: A + + Uniform = object + a, b: float + + ClosureVar[A] = proc(rng: var RNG): A + +proc sample[A](rng: var RNG, c: Constant[A]): A = c.value + +proc sample(rng: var RNG, u: Uniform): float = u.a + (u.b - u.a) * rng.random() + +proc sample[A](rng: var RNG, c: ClosureVar[A]): A = c(rng) + +proc constant[A](a: A): Constant[A] = Constant[A](value: a) + +proc uniform(a, b: float): Uniform = Uniform(a: a, b: b) + +proc lift1[A, B](f: proc(a: A): B, r: RandomVar[A]): ClosureVar[B] = + proc inner(rng: var RNG): B = f(rng.sample(r)) + + return inner + +when isMainModule: + proc sq(x: float): float = x * x + + let + c = constant(3) + u = uniform(2, 18) + t = lift1(sq, u) + + var rng: RNG + + echo(c is RandomVar[int]) + echo(u is RandomVar[float]) + echo(t is RandomVar[float]) + + echo rng.sample(c) + echo rng.sample(u) + echo rng.sample(t) + diff --git a/tests/concepts/twrapconcept.nim b/tests/concepts/twrapconcept.nim new file mode 100644 index 000000000..25a855e34 --- /dev/null +++ b/tests/concepts/twrapconcept.nim @@ -0,0 +1,22 @@ +discard """ + errormsg: "type mismatch: got (string)" + line: 21 + nimout: "twrapconcept.nim(11, 5) Foo: concept predicate failed" +""" + +# https://github.com/nim-lang/Nim/issues/5127 + +type + Foo = concept foo + foo.get is int + + FooWrap[F: Foo] = object + foo: F + +proc get(x: int): int = x + +proc wrap[F: Foo](foo: F): FooWrap[F] = FooWrap[F](foo: foo) + +let x = wrap(12) +let y = wrap "string" + diff --git a/tests/errmsgs/tconceptconstraint.nim b/tests/errmsgs/tconceptconstraint.nim new file mode 100644 index 000000000..c1f0b94eb --- /dev/null +++ b/tests/errmsgs/tconceptconstraint.nim @@ -0,0 +1,21 @@ +discard """ + errormsg: "cannot instantiate B" + line: 20 + nimout: ''' +got: (type string) +but expected: (T: A) +''' +""" + +type + A = concept c + advance(c) + + B[T: A] = object + child: ref B[T] + +proc advance(x: int): int = x + 1 + +var a: B[int] +var b: B[string] + diff --git a/tests/errmsgs/tgenericconstraint.nim b/tests/errmsgs/tgenericconstraint.nim new file mode 100644 index 000000000..9129d257b --- /dev/null +++ b/tests/errmsgs/tgenericconstraint.nim @@ -0,0 +1,15 @@ +discard """ + errormsg: "cannot instantiate B" + line: 14 + nimout: ''' +got: (type int) +but expected: (T: string or float) +''' +""" + +type + B[T: string|float] = object + child: ref B[T] + +var b: B[int] + diff --git a/tests/fields/timplicitfieldswithpartial.nim b/tests/fields/timplicitfieldswithpartial.nim new file mode 100644 index 000000000..996912a1a --- /dev/null +++ b/tests/fields/timplicitfieldswithpartial.nim @@ -0,0 +1,19 @@ +discard """ + output: '''(foo: 38, other: string here) +43''' +""" + +type + Base = ref object of RootObj + Foo {.partial.} = ref object of Base + +proc my(f: Foo) = + #var f.next = f + let f.foo = 38 + let f.other = "string here" + echo f[] + echo f.foo + 5 + +var g: Foo +new(g) +my(g) diff --git a/tests/generics/tfakedependenttypes.nim b/tests/generics/tfakedependenttypes.nim new file mode 100644 index 000000000..cd4be806c --- /dev/null +++ b/tests/generics/tfakedependenttypes.nim @@ -0,0 +1,61 @@ +discard """ +output: ''' +U[3] +U[(f: 3)] +U[[3]] +''' +""" + +# https://github.com/nim-lang/Nim/issues/5106 + +import typetraits + +block: + type T = distinct int + + proc `+`(a, b: T): T = + T(int(a) + int(b)) + + type U[F: static[T]] = distinct int + + proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] = + U[P1 + P2](int(a) + int(b)) + + var a = U[T(1)](1) + var b = U[T(2)](2) + var c = a + b + echo c.type.name + +block: + type T = object + f: int + + proc `+`(a, b: T): T = + T(f: a.f + b.f) + + type U[F: static[T]] = distinct int + + proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] = + U[P1 + P2](int(a) + int(b)) + + var a = U[T(f: 1)](1) + var b = U[T(f: 2)](2) + var c = a + b + echo c.type.name + +block: + type T = distinct array[0..0, int] + + proc `+`(a, b: T): T = + T([array[0..0, int](a)[0] + array[0..0, int](b)[0]]) + + type U[F: static[T]] = distinct int + + proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] = + U[P1 + P2](int(a) + int(b)) + + var a = U[T([1])](1) + var b = U[T([2])](2) + var c = a + b + echo c.type.name + diff --git a/tests/generics/tgenericsdefaultvalues.nim b/tests/generics/tgenericsdefaultvalues.nim new file mode 100644 index 000000000..2604c1031 --- /dev/null +++ b/tests/generics/tgenericsdefaultvalues.nim @@ -0,0 +1,14 @@ +discard """ +output: "12" +""" + +# https://github.com/nim-lang/Nim/issues/5864 + +proc defaultStatic(s: openarray, N: static[int] = 1): int = N +proc defaultGeneric[T](a: T = 2): int = a + +let a = [1, 2, 3, 4].defaultStatic() +let b = defaultGeneric() + +echo a, b + diff --git a/tests/generics/tproctypecache_falsepositive.nim b/tests/generics/tproctypecache_falsepositive.nim new file mode 100644 index 000000000..4f24a1fc8 --- /dev/null +++ b/tests/generics/tproctypecache_falsepositive.nim @@ -0,0 +1,17 @@ + +import asyncdispatch + +type + Callback = proc() {.closure, gcsafe.} + GameState = ref object + playerChangeHandlers: seq[Callback] + +#proc dummy() = +# var x = newSeq[proc() {.cdecl, gcsafe.}]() + +proc newGameState(): GameState = + result = GameState( + playerChangeHandlers: newSeq[Callback]() # this fails + ) + +#dummy() diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim new file mode 100644 index 000000000..9b4774e9b --- /dev/null +++ b/tests/generics/treentranttypes.nim @@ -0,0 +1,111 @@ +discard """ +output: ''' +(Field0: 10, Field1: (Field0: test, Field1: 1.2)) +3x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0], [2.0, 0.0, 5.0]] + +2x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]] + +2x3 Literal [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]] + +2x3 Matrix [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + +2x2 ArrayArray[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + +2x3 ArrayVector[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + +2x3 VectorVector [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + +2x3 VectorArray [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] + +@[1, 2] +@[1, 2] +@[1, 2]@[3, 4] +@[1, 2]@[3, 4] +''' +""" + +# https://github.com/nim-lang/Nim/issues/5962 + +type + ArrayLike[A, B] = (A, B) + VectorLike*[SIZE, T] = ArrayLike[SIZE, T] + MatrixLike*[M, N, T] = VectorLike[M, VectorLike[N, T]] + +proc tupleTest = + let m: MatrixLike[int, string, float] = (10, ("test", 1.2)) + echo m + +tupleTest() + +type + Vector*[K: static[int], T] = + array[K, T] + + Matrix*[M: static[int]; N: static[int]; T] = + Vector[M, Vector[N, T]] + +proc arrayTest = + # every kind of square matrix works just fine + let mat_good: Matrix[3, 3, float] = [[0.0, 2.0, 3.0], + [2.0, 0.0, 5.0], + [2.0, 0.0, 5.0]] + echo "3x3 Matrix ", repr(mat_good) + + # this does not work with explicit type signature (the matrix seems to always think it is NxN instead) + let mat_fail: Matrix[2, 3, float] = [[0.0, 2.0, 3.0], + [2.0, 0.0, 5.0]] + echo "2x3 Matrix ", repr(mat_fail) + + # this literal seems to work just fine + let mat_also_good = [[0.0, 2.0, 3.0], + [2.0, 0.0, 5.0]] + + echo "2x3 Literal ", repr(mat_also_good) + + # but making a named type out of this leads to pretty nasty runtime behavior + var mat_fail_runtime: Matrix[2, 3, float] + echo "2x3 Matrix ", repr(mat_fail_runtime) + + # cutting out the matrix type middle man seems to solve our problem + var mat_ok_runtime: array[2, array[3, float]] + echo "2x2 ArrayArray", repr(mat_ok_runtime) + + # this is fine too + var mat_ok_runtime_2: array[2, Vector[3, float]] + echo "2x3 ArrayVector", repr(mat_ok_runtime_2) + + # here we are in trouble again + var mat_fail_runtime_2: Vector[2, Vector[3, float]] + echo "2x3 VectorVector ", repr(mat_fail_runtime_2) + + # and here we are fine again + var mat_ok_runtime_3: Vector[2, array[3, float]] + echo "2x3 VectorArray ", repr(mat_ok_runtime_3) + +arrayTest() + +# https://github.com/nim-lang/Nim/issues/5756 + +type + Vec*[N : static[int]] = object + arr*: array[N, int32] + + Mat*[M,N: static[int]] = object + arr*: array[M, Vec[N]] + +proc vec2*(x,y:int32) : Vec[2] = + result.arr = [x,y] + +proc mat2*(a,b: Vec[2]): Mat[2,2] = + result.arr = [a,b] + +const a = vec2(1,2) +echo @(a.arr) +let x = a +echo @(x.arr) + +const b = mat2(vec2(1, 2), vec2(3, 4)) +echo @(b.arr[0].arr), @(b.arr[1].arr) +let y = b +echo @(y.arr[0].arr), @(y.arr[1].arr) + diff --git a/tests/js/tjshello.nim b/tests/js/tjshello.nim new file mode 100644 index 000000000..19e0b90ae --- /dev/null +++ b/tests/js/tjshello.nim @@ -0,0 +1,10 @@ +discard """ + output: "Hello World" + maxcodesize: 1000 + ccodecheck: "!@'function'" +""" + +import jsconsole + +console.log "Hello World" + diff --git a/tests/js/tseqops.nim b/tests/js/tseqops.nim new file mode 100644 index 000000000..d10e1ca6a --- /dev/null +++ b/tests/js/tseqops.nim @@ -0,0 +1,51 @@ +discard """ + output: '''(x: 0, y: 0) +(x: 5, y: 0) +@[(x: 2, y: 4), (x: 4, y: 5), (x: 4, y: 5)] +@[(a: 3, b: 3), (a: 1, b: 1), (a: 2, b: 2)] +''' +""" + +# bug #4139 + +type + TestO = object + x, y: int + +proc onLoad() = + var test: seq[TestO] = @[] + var foo = TestO(x: 0, y: 0) + test.add(foo) + foo.x = 5 + echo(test[0]) + echo foo + +onLoad() + +# 'setLen' bug (part of bug #5933) +type MyObj = object + x: cstring + y: int + +proc foo(x: var seq[MyObj]) = + let L = x.len + x.setLen L + 1 + x[L] = x[1] + +var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)] +foo(s) +echo s + +# bug #5933 +import sequtils + +type + Test = object + a: cstring + b: int + +var test = @[Test(a: "1", b: 1), Test(a: "2", b: 2)] + +test.insert(@[Test(a: "3", b: 3)], 0) + +echo test diff --git a/tests/macros/tnewlit.nim b/tests/macros/tnewlit.nim new file mode 100644 index 000000000..69245d076 --- /dev/null +++ b/tests/macros/tnewlit.nim @@ -0,0 +1,140 @@ +import macros + +type + MyType = object + a : int + b : string + +macro test_newLit_MyType: untyped = + let mt = MyType(a: 123, b:"foobar") + result = newLit(mt) + +doAssert test_newLit_MyType == MyType(a: 123, b:"foobar") + +macro test_newLit_array: untyped = + let arr = [1,2,3,4,5] + result = newLit(arr) + +doAssert test_newLit_array == [1,2,3,4,5] + +macro test_newLit_seq_int: untyped = + let s: seq[int] = @[1,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[int] = test_newLit_seq_int + doAssert tmp == @[1,2,3,4,5] + +macro test_newLit_seq_int8: untyped = + let s: seq[int8] = @[1'i8,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[int8] = test_newLit_seq_int8 + doAssert tmp == @[1'i8,2,3,4,5] + +macro test_newLit_seq_int16: untyped = + let s: seq[int16] = @[1'i16,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[int16] = test_newLit_seq_int16 + doAssert tmp == @[1'i16,2,3,4,5] + +macro test_newLit_seq_int32: untyped = + let s: seq[int32] = @[1'i32,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[int32] = test_newLit_seq_int32 + doAssert tmp == @[1'i32,2,3,4,5] + +macro test_newLit_seq_int64: untyped = + let s: seq[int64] = @[1'i64,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[int64] = test_newLit_seq_int64 + doAssert tmp == @[1'i64,2,3,4,5] + +macro test_newLit_seq_uint: untyped = + let s: seq[uint] = @[1u,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[uint] = test_newLit_seq_uint + doAssert tmp == @[1u,2,3,4,5] + +macro test_newLit_seq_uint8: untyped = + let s: seq[uint8] = @[1'u8,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[uint8] = test_newLit_seq_uint8 + doAssert tmp == @[1'u8,2,3,4,5] + +macro test_newLit_seq_uint16: untyped = + let s: seq[uint16] = @[1'u16,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[uint16] = test_newLit_seq_uint16 + doAssert tmp == @[1'u16,2,3,4,5] + +macro test_newLit_seq_uint32: untyped = + let s: seq[uint32] = @[1'u32,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[uint32] = test_newLit_seq_uint32 + doAssert tmp == @[1'u32,2,3,4,5] + +macro test_newLit_seq_uint64: untyped = + let s: seq[uint64] = @[1'u64,2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[uint64] = test_newLit_seq_uint64 + doAssert tmp == @[1'u64,2,3,4,5] + +macro test_newLit_seq_float: untyped = + let s: seq[float] = @[1.0, 2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[float] = test_newLit_seq_float + doAssert tmp == @[1.0, 2,3,4,5] + +macro test_newLit_seq_float32: untyped = + let s: seq[float32] = @[1.0'f32, 2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[float32] = test_newLit_seq_float32 + doAssert tmp == @[1.0'f32, 2,3,4,5] + +macro test_newLit_seq_float64: untyped = + let s: seq[float64] = @[1.0'f64, 2,3,4,5] + result = newLit(s) + +block: + let tmp: seq[float64] = test_newLit_seq_float64 + doAssert tmp == @[1.0'f64, 2,3,4,5] + +macro test_newLit_tuple: untyped = + let tup: tuple[a:int,b:string] = (a: 123, b: "223") + result = newLit(tup) + +doAssert test_newLit_tuple == (a: 123, b: "223") + +type + ComposedType = object + mt: MyType + arr: array[4,int] + data: seq[byte] + +macro test_newLit_ComposedType: untyped = + let ct = ComposedType(mt: MyType(a: 123, b:"abc"), arr: [1,2,3,4], data: @[1.byte, 3, 7, 127]) + result = newLit(ct) + +doAssert test_newLit_ComposedType == ComposedType(mt: MyType(a: 123, b:"abc"), arr: [1,2,3,4], data: @[1.byte, 3, 7, 127]) diff --git a/tests/macros/tnodecompare.nim b/tests/macros/tnodecompare.nim index 3870c7559..b9cf7df48 100644 --- a/tests/macros/tnodecompare.nim +++ b/tests/macros/tnodecompare.nim @@ -1,33 +1,33 @@ -discard """ -output: '''true -false -true -false -true -false -true -false''' -""" - import macros +static: + let nodeA = newCommentStmtNode("this is a comment") + doAssert nodeA.repr == "## this is a comment" + doAssert nodeA.strVal == "this is a comment" + doAssert $nodeA == "this is a comment" + + let nodeB = newCommentStmtNode("this is a comment") + doAssert nodeA == nodeB + nodeB.strVal = "this is a different comment" + doAssert nodeA != nodeB + macro test(a: typed, b: typed): expr = newLit(a == b) -echo test(1, 1) -echo test(1, 2) +doAssert test(1, 1) == true +doAssert test(1, 2) == false type Obj = object of RootObj Other = object of RootObj -echo test(Obj, Obj) -echo test(Obj, Other) +doAssert test(Obj, Obj) == true +doAssert test(Obj, Other) == false var a, b: int -echo test(a, a) -echo test(a, b) +doAssert test(a, a) == true +doAssert test(a, b) == false macro test2: expr = newLit(bindSym"Obj" == bindSym"Obj") @@ -35,5 +35,5 @@ macro test2: expr = macro test3: expr = newLit(bindSym"Obj" == bindSym"Other") -echo test2() -echo test3() +doAssert test2() == true +doAssert test3() == false diff --git a/tests/metatype/tmatrix4.nim b/tests/metatype/tmatrix4.nim new file mode 100644 index 000000000..207d76fed --- /dev/null +++ b/tests/metatype/tmatrix4.nim @@ -0,0 +1,39 @@ +import math + +type + TMatrix*[T; R, C: static[int]] = array[R, array[C, T]] ## Row major matrix type. + TMat4* = TMatrix[float32, 4, 4] + TVector*[T; C: static[int]] = array[C, T] + TVec4* = TVector[float32, 4] + +template row*[T; R, C: static[int]](m: TMatrix[T, R, C], rowidx: range[0..R-1]): TVector[T, R] = + m[rowidx] + +proc col*[T; R, C: static[int]](m: TMatrix[T, R, C], colidx: range[0..C-1]): TVector[T, C] {.noSideEffect.} = + for i in low(m)..high(m): + result[i] = m[i][colidx] + +proc dot(lhs, rhs: TVector): float32 = + for i in low(rhs)..high(rhs): + result += lhs[i] * rhs[i] + +proc `*`*[T; R, N, C: static[int]](a: TMatrix[T, R, N], b: TMatrix[T, N, C]): TMatrix[T, R, C] {.noSideEffect.} = + for i in low(a)..high(a): + for j in low(a[i])..high(a[i]): + result[i][j] = dot(a.row(i), b.col(j)) + +proc translate*(v: TVec4): TMat4 {.noSideEffect.} = + result = [[1f32, 0f32, 0f32, 0f32], + [0f32, 1f32, 0f32, 0f32], + [0f32, 0f32, 1f32, 0f32], + [v[0], v[1], v[2], 1f32]] + +proc rotatex*(angle: float): TMat4 = + result = [[1f32, 0f32, 0f32, 0f32], + [0f32, cos(angle).float32, sin(angle).float32, 0f32], + [0f32, -sin(angle).float32, cos(angle).float32, 0f32], + [0f32, 0f32, 0f32, 1f32]] + +proc orbitxAround(point: TVec4, angle: float): TMat4 = + result = translate(point)*rotatex(angle)*translate(point) + diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index 11653e563..69b62e4a6 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -1,6 +1,6 @@ discard """ file: "tstaticparams.nim" - output: "abracadabra\ntest\n3\n15\n4\n2\nfloat\n3\nfloat\nyin\nyang" + output: "abracadabra\ntest\n3\n15\n4\n2\nfloat\n3\nfloat\nyin\nyang\n2\n4\n4\n2\n3" """ type @@ -140,3 +140,36 @@ dontBind1 bb_2 dontBind2 bb_1 dontBind2 bb_2 +# https://github.com/nim-lang/Nim/issues/4524 +const + size* = 2 + +proc arraySize[N: static[int]](A: array[N, int]): int = + result = A.high - A.low + 1 + +var A: array[size, int] = [1, 2] +echo arraySize(A) + +# https://github.com/nim-lang/Nim/issues/3153 + +proc outSize1[M: static[int], A](xs: array[M, A]): int = M +echo outSize1([1, 2, 3, 4]) + +type + Arr[N: static[int], A] = array[N, A] + +proc outSize2[M: static[int], A](xs: Arr[M, A]): int = M +echo outSize2([1, 2, 3, 4]) # 4 + +echo outSize2([ + [1, 2, 3], + [4, 5, 6] +]) # 2 + +proc inSize[M, N: static[int]](xs: Arr[M, Arr[N, int]]): int = N + +echo inSize([ + [1, 2, 3], + [4, 5, 6] +]) + diff --git a/tests/metatype/tstaticvector.nim b/tests/metatype/tstaticvector.nim index 69ee0e935..1a7bdeafe 100644 --- a/tests/metatype/tstaticvector.nim +++ b/tests/metatype/tstaticvector.nim @@ -2,7 +2,9 @@ discard """ output: '''0 0 2 -100''' +100 +30.0 [data = [2.0]] +''' """ type @@ -16,13 +18,12 @@ type proc foo*[N, T](a: StaticVector[N, T]): T = 0.T proc foobar*[N, T](a, b: StaticVector[N, T]): T = 0.T - var a: StaticVector[3, int] echo foo(a) # OK echo foobar(a, a) # <--- hangs compiler -# bug #3112 +# https://github.com/nim-lang/Nim/issues/3112 type Vector[N: static[int]] = array[N, float64] @@ -30,10 +31,45 @@ type a: Vector[Na] b: Vector[Nb] -when isMainModule: - var v: TwoVectors[2, 100] - echo v[0].len - echo v[1].len - #let xx = 50 - v[1][50] = 0.0 +var v: TwoVectors[2, 100] +echo v[0].len +echo v[1].len +#let xx = 50 +v[1][50] = 0.0 + +# https://github.com/nim-lang/Nim/issues/1051 + +type + TMatrix[N,M: static[int], T] = object + data: array[0..M*N-1, T] + + TMat4f = TMatrix[4,4,float32] + TVec3f = TMatrix[1,3,float32] + TVec4f = TMatrix[1,4,float32] + + TVec[N: static[int]; T] = TMatrix[1,N,T] + +proc dot*(a, b: TVec): TVec.T = + #assert(a.data.len == b.data.len) + for i in 1..a.data.len: + result += a.data[i-1] * b.data[i-1] + +proc row*(a: TMatrix; i: int): auto = + result = TVec[TMatrix.M, TMatrix.T]() + for idx in 1 .. TMatrix.M: + result.data[idx-1] = a.data[(TMatrix.N * (idx-1)) + (i-1)] + +proc col*(a: TMatrix; j: int): auto = + result = TVec[TMatrix.N, TMatrix.T]() + for idx in 0 .. <TMatrix.N: + result.data[idx] = a.data[(TMatrix.N * (idx)) + (j-1)] + +proc mul*(a: TMat4f; b: TMat4f): TMat4f = + for i in 1..4: + for j in 1..4: + result.data[(4 * (j-1)) + (i-1)] = dot(row(a,i), col(b,j)) + +var test = TVec4f(data: [1.0'f32, 2.0'f32, 3.0'f32, 4.0'f32]) + +echo dot(test,test), " ", repr(col(test, 2)) diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim new file mode 100644 index 000000000..4e27040fb --- /dev/null +++ b/tests/misc/tcast.nim @@ -0,0 +1,23 @@ +discard """ + output: ''' +Hello World +Hello World''' +""" +type MyProc = proc() {.cdecl.} +type MyProc2 = proc() {.nimcall.} +type MyProc3 = proc() #{.closure.} is implicit + +proc testProc() = echo "Hello World" + +proc callPointer(p: pointer) = + # can cast to proc(){.cdecl.} + let ffunc0 = cast[MyProc](p) + # can cast to proc(){.nimcall.} + let ffunc1 = cast[MyProc2](p) + # cannot cast to proc(){.closure.} + doAssert(not compiles(cast[MyProc3](p))) + + ffunc0() + ffunc1() + +callPointer(cast[pointer](testProc)) diff --git a/tests/misc/tparseopt.nim b/tests/misc/tparseopt.nim new file mode 100644 index 000000000..1f8375dfd --- /dev/null +++ b/tests/misc/tparseopt.nim @@ -0,0 +1,57 @@ +discard """ + file: "tparseopt.nim" + output: ''' +parseopt +first round +kind: cmdLongOption key:val -- left: +second round +kind: cmdLongOption key:val -- left: +kind: cmdLongOption key:val -- debug:3 +kind: cmdShortOption key:val -- l:4 +kind: cmdShortOption key:val -- r:2 +parseopt2 +first round +kind: cmdLongOption key:val -- left: +second round +kind: cmdLongOption key:val -- left: +kind: cmdLongOption key:val -- debug:3 +kind: cmdShortOption key:val -- l:4 +kind: cmdShortOption key:val -- r:2''' +""" +from parseopt import nil +from parseopt2 import nil + + +block: + echo "parseopt" + for kind, key, val in parseopt.getopt(): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + + # pass custom cmdline arguments + echo "first round" + var argv = "--left --debug:3 -l=4 -r:2" + var p = parseopt.initOptParser(argv) + for kind, key, val in parseopt.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + break + # reset getopt iterator and check arguments are returned correctly. + echo "second round" + for kind, key, val in parseopt.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + +block: + echo "parseopt2" + for kind, key, val in parseopt2.getopt(): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + + # pass custom cmdline arguments + echo "first round" + var argv: seq[string] = @["--left", "--debug:3", "-l=4", "-r:2"] + var p = parseopt2.initOptParser(argv) + for kind, key, val in parseopt2.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + break + # reset getopt iterator and check arguments are returned correctly. + echo "second round" + for kind, key, val in parseopt2.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val diff --git a/tests/modules/tmismatchedvisibility.nim b/tests/modules/tmismatchedvisibility.nim index 325c729c0..91b639a27 100644 --- a/tests/modules/tmismatchedvisibility.nim +++ b/tests/modules/tmismatchedvisibility.nim @@ -1,6 +1,6 @@ discard """ line: 8 - errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)' has non-public forward declaration in " + errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6,5)]' has non-public forward declaration in " """ proc foo(a: int): int diff --git a/tests/overload/tstaticoverload.nim b/tests/overload/tstaticoverload.nim new file mode 100644 index 000000000..33ca49e56 --- /dev/null +++ b/tests/overload/tstaticoverload.nim @@ -0,0 +1,30 @@ +discard """ +output: ''' +dynamic: let +dynamic: var +static: const +static: literal +static: constant folding +static: static string +''' +""" + +proc foo(s: string) = + echo "dynamic: ", s + +proc foo(s: static[string]) = + echo "static: ", s + +let l = "let" +var v = "var" +const c = "const" + +type staticString = static[string] + +foo(l) +foo(v) +foo(c) +foo("literal") +foo("constant" & " " & "folding") +foo(staticString("static string")) + diff --git a/tests/pragmas/tused.nim b/tests/pragmas/tused.nim index 4a317f874..389863aef 100644 --- a/tests/pragmas/tused.nim +++ b/tests/pragmas/tused.nim @@ -1,7 +1,7 @@ discard """ nimout: ''' compile start -tused.nim(15, 8) Hint: 'tused.echoSub(a: int, b: int)' is declared but not used [XDeclaredButNotUsed] +tused.nim(15, 8) Hint: 'tused.echoSub(a: int, b: int)[declared in tused.nim(15,7)]' is declared but not used [XDeclaredButNotUsed] compile end''' output: "8\n8" """ diff --git a/tests/statictypes/t3784.nim b/tests/statictypes/t3784.nim new file mode 100644 index 000000000..3414d9802 --- /dev/null +++ b/tests/statictypes/t3784.nim @@ -0,0 +1,20 @@ +discard """ +output: "T[1, 1]" +""" + +# https://github.com/nim-lang/Nim/issues/3784 + +import typetraits + +type + S[N: static[int]] = object + T[A,B: static[int]] = object + + C = S[1] + +var + x: T[1,1] + y: T[C.N, C.N] + +echo y.type.name + diff --git a/tests/statictypes/texplicitprocparams.nim b/tests/statictypes/texplicitprocparams.nim new file mode 100644 index 000000000..d1e717dbf --- /dev/null +++ b/tests/statictypes/texplicitprocparams.nim @@ -0,0 +1,19 @@ +discard """ +output: ''' +(x: 100) +5 +''' +""" + +type + OdArray*[As: static[int], T] = object + x: int + +proc initOdArray*[As: static[int], T](len: int): OdArray[As, T] = + result.x = len + +echo initOdArray[10, int](100) + +proc doStatic[N: static[int]](): int = N +echo doStatic[5]() + diff --git a/tests/statictypes/tpassthruarith.nim b/tests/statictypes/tpassthruarith.nim new file mode 100644 index 000000000..90fc7824c --- /dev/null +++ b/tests/statictypes/tpassthruarith.nim @@ -0,0 +1,54 @@ +discard """ +output: ''' +[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] + +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + +[1, 2, 3, 4] +''' +""" + +# https://github.com/nim-lang/Nim/issues/4880 + +proc `^^`(x: int): int = x * 2 + +type + Foo[x: static[int]] = array[x, int] + Bar[a, b: static[int]] = array[b, Foo[^^a]] + +var x: Bar[2, 3] +echo repr(x) + +# https://github.com/nim-lang/Nim/issues/2730 + +type + Matrix[M,N: static[int]] = distinct array[0..(M*N - 1), int] + +proc bigger[M,N](m: Matrix[M,N]): Matrix[(M * N) div 8, (M * N)] = + discard + +proc bigger2[M,N](m: Matrix[M,N]): Matrix[M * 2, N * 2] = + discard + +var m : Matrix[4, 4] +var n = bigger(m) +var o = bigger2(m) + +echo repr(m) +echo repr(n) +echo repr(o) + +type + Vect[N: static[int], A] = array[N, A] + +proc push[N: static[int], A](a: Vect[N, A], x: A): Vect[N + 1, A] = + for n in 0 .. < N: + result[n] = a[n] + result[N] = x + +echo repr(push([1, 2, 3], 4)) + diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 538582ba8..581308a7e 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,4 +1,15 @@ -import math, random +discard """ + action: run + output: '''[Suite] random int + +[Suite] random float + +[Suite] ^ + +''' +""" + +import math, random, os import unittest import sets @@ -26,6 +37,7 @@ suite "random int": test "randomize() again gives new numbers": randomize() var rand1 = random(1000000) + os.sleep(200) randomize() var rand2 = random(1000000) check rand1 != rand2 @@ -55,7 +67,16 @@ suite "random float": test "randomize() again gives new numbers": randomize() var rand1:float = random(1000000.0) + os.sleep(200) randomize() var rand2:float = random(1000000.0) check rand1 != rand2 +suite "^": + test "compiles for valid types": + check: compiles(5 ^ 2) + check: compiles(5.5 ^ 2) + check: compiles(5.5 ^ 2.int8) + check: compiles(5.5 ^ 2.uint) + check: compiles(5.5 ^ 2.uint8) + check: not compiles(5.5 ^ 2.2) \ No newline at end of file diff --git a/tests/stdlib/torderedtable.nim b/tests/stdlib/torderedtable.nim new file mode 100644 index 000000000..91a916930 --- /dev/null +++ b/tests/stdlib/torderedtable.nim @@ -0,0 +1,18 @@ +import tables, random +var t = initOrderedTable[int,string]() + +# this tests issue #5917 +var data = newSeq[int]() +for i in 0..<1000: + var x = random(1000) + if x notin t: data.add(x) + t[x] = "meh" + +# this checks that keys are re-inserted +# in order when table is enlarged. +var i = 0 +for k, v in t: + doAssert(k == data[i]) + doAssert(v == "meh") + inc(i) + diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index ab24acc70..ed435465a 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -52,6 +52,7 @@ type exitCode*: int msg*: string ccodeCheck*: string + maxCodeSize*: int err*: TResultEnum substr*, sortoutput*: bool targets*: set[TTarget] @@ -109,6 +110,7 @@ proc specDefaults*(result: var TSpec) = result.tfile = "" result.tline = 0 result.tcolumn = 0 + result.maxCodeSize = 0 proc parseTargets*(value: string): set[TTarget] = for v in value.normalize.split: @@ -180,6 +182,7 @@ proc parseSpec*(filename: string): TSpec = else: result.cmd = e.value of "ccodecheck": result.ccodeCheck = e.value + of "maxcodesize": discard parseInt(e.value, result.maxCodeSize) of "target", "targets": for v in e.value.normalize.split: case v diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 0f74de013..b83eb668a 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -212,20 +212,31 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = proc generatedFile(path, name: string, target: TTarget): string = let ext = targetToExt[target] result = path / "nimcache" / - (if target == targetJS: path.splitPath.tail & "_" else: "compiler_") & + (if target == targetJS: "" else: "compiler_") & name.changeFileExt(ext) -proc codegenCheck(test: TTest, check: string, given: var TSpec) = +proc needsCodegenCheck(spec: TSpec): bool = + result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0 + +proc codegenCheck(test: TTest, spec: TSpec, expectedMsg: var string, + given: var TSpec) = try: let (path, name, _) = test.name.splitFile let genFile = generatedFile(path, name, test.target) let contents = readFile(genFile).string - if check[0] == '\\': - # little hack to get 'match' support: - if not contents.match(check.peg): + let check = spec.ccodeCheck + if check.len > 0: + if check[0] == '\\': + # little hack to get 'match' support: + if not contents.match(check.peg): + given.err = reCodegenFailure + elif contents.find(check.peg) < 0: given.err = reCodegenFailure - elif contents.find(check.peg) < 0: + expectedMsg = check + if spec.maxCodeSize > 0 and contents.len > spec.maxCodeSize: given.err = reCodegenFailure + given.msg = "generated code size: " & $contents.len + expectedMsg = "max allowed size: " & $spec.maxCodeSize except ValueError: given.err = reInvalidPeg echo getCurrentExceptionMsg() @@ -248,9 +259,8 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec; var expectedmsg: string = "" var givenmsg: string = "" if given.err == reSuccess: - if expected.ccodeCheck.len > 0: - codegenCheck(test, expected.ccodeCheck, given) - expectedmsg = expected.ccodeCheck + if expected.needsCodegenCheck: + codegenCheck(test, expected, expectedmsg, given) givenmsg = given.msg if expected.nimout.len > 0: expectedmsg = expected.nimout diff --git a/tests/typerel/ttypedesc_as_genericparam1.nim b/tests/typerel/ttypedesc_as_genericparam1.nim index 677bf6fc8..88c0509b2 100644 --- a/tests/typerel/ttypedesc_as_genericparam1.nim +++ b/tests/typerel/ttypedesc_as_genericparam1.nim @@ -1,6 +1,6 @@ discard """ line: 6 - errormsg: "type mismatch: got (typedesc[int])" + errormsg: "type mismatch: got (type int)" """ # bug #3079, #1146 echo repr(int) diff --git a/tests/typerel/ttypenoval.nim b/tests/typerel/ttypenoval.nim index d5b91fbca..eabca48f6 100644 --- a/tests/typerel/ttypenoval.nim +++ b/tests/typerel/ttypenoval.nim @@ -1,7 +1,7 @@ discard """ file: "ttypenoval.nim" line: 38 - errormsg: "type mismatch: got (typedesc[int]) but expected 'int'" + errormsg: "type mismatch: got (type int) but expected 'int'" """ # A min-heap. diff --git a/tests/types/t1252.nim b/tests/types/t1252.nim new file mode 100644 index 000000000..c6a12e9f7 --- /dev/null +++ b/tests/types/t1252.nim @@ -0,0 +1,12 @@ +discard """ + output: '''true +true +true +true +''' +""" + +echo float32 isnot float64 +echo float32 isnot float +echo int32 isnot int64 +echo int32 isnot int diff --git a/tests/types/thard_tyforward.nim b/tests/types/thard_tyforward.nim index 7131cd64b..8f606a0f9 100644 --- a/tests/types/thard_tyforward.nim +++ b/tests/types/thard_tyforward.nim @@ -2,8 +2,8 @@ type Bar[T] = Foo[T, T] Baz[T] = proc (x: Foo[T, T]) - GenericAlias[T] = Foo[T] - GenericAlias2[T] = Foo[Baz[T]] + GenericAlias[T] = Foo[T, T] + GenericAlias2[T] = Foo[Baz[T], T] Concrete1 = Foo[int, float] Concrete2 = proc(x: proc(a: Foo[int, float])) diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index cca03cd62..e5a41e3c2 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -72,3 +72,11 @@ static: echo "OK" +static: + echo "testing creation of comment node" + var docComment: NimNode = newNimNode(nnkCommentStmt) + docComment.strVal = "This is a doc comment" + + assertEq repr(docComment), "## This is a doc comment" + + echo "OK" diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 67f5e2b33..e4568dc3a 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -679,11 +679,14 @@ RunProgram="tools\downloader.exe" if execShellCmd("7z a -sfx7zS2.sfx -t7z $1.exe $1" % proj) != 0: echo("External program failed (7z)") else: - if execShellCmd("XZ_OPT=-9 gtar Jcf $1.tar.xz $1 --exclude=.DS_Store" % + if execShellCmd("gtar cf $1.tar $1 --exclude=.DS_Store" % proj) != 0: # try old 'tar' without --exclude feature: - if execShellCmd("XZ_OPT=-9 tar Jcf $1.tar.xz $1" % proj) != 0: + if execShellCmd("tar cf $1.tar $1" % proj) != 0: echo("External program failed") + + if execShellCmd("xz -9f $1.tar" % proj) != 0: + echo("External program failed") finally: setCurrentDir(oldDir) |