diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-07-13 04:48:22 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-07-13 04:48:22 +0200 |
commit | 2b862b74e0b0b7b4a18f4262356289fb921eaf0c (patch) | |
tree | 8f41b7355f6d791d6485e8225d6a5cb2f80ca7d6 | |
parent | a5695c13afabac6e67ff677d564b6d1a6aeb1af4 (diff) | |
parent | 0c271f54208c7ba0bac6ad2da87f60e7c6d8e37c (diff) | |
download | Nim-2b862b74e0b0b7b4a18f4262356289fb921eaf0c.tar.gz |
Merge branch 'devel' into araq
165 files changed, 4583 insertions, 931 deletions
diff --git a/appveyor.yml b/appveyor.yml index f27429681..f40fb3e4c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -56,7 +56,7 @@ build_script: test_script: - tests\testament\tester --pedantic all -d:nimCoroutines - - koch csource - - koch zip +# - koch csource +# - koch zip deploy: off diff --git a/compiler/ast.nim b/compiler/ast.nim index 487b5d1a7..ac917346f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -224,7 +224,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # already 32 flags! + TSymFlag* = enum # already 33 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -453,9 +453,10 @@ 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: 30) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) tfVarargs, # procedure has C styled varargs # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects @@ -508,6 +509,9 @@ type tfTriggersCompileTime # uses the NimNode type which make the proc # implicitly '.compiletime' tfRefsAnonObj # used for 'ref object' and 'ptr object' + tfCovariant # covariant generic param mimicing a ptr type + tfWeakCovariant # covariant generic param mimicing a seq/array type + tfContravariant # contravariant generic param TTypeFlags* = set[TTypeFlag] @@ -956,7 +960,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 @@ -1007,15 +1012,17 @@ proc add*(father, son: PNode) = if isNil(father.sons): father.sons = @[] add(father.sons, son) -proc `[]`*(n: PNode, i: int): PNode {.inline.} = - result = n.sons[i] +type Indexable = PNode | PType + +template `[]`*(n: Indexable, i: int): Indexable = + n.sons[i] template `-|`*(b, s: untyped): untyped = (if b >= 0: b else: s.len + b) # son access operators with support for negative indices -template `{}`*(n: PNode, i: int): untyped = n[i -| n] -template `{}=`*(n: PNode, i: int, s: PNode) = +template `{}`*(n: Indexable, i: int): untyped = n[i -| n] +template `{}=`*(n: Indexable, i: int, s: Indexable) = n.sons[i -| n] = s when defined(useNodeIds): diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 1fc8b7cea..196ac8690 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)]) @@ -574,12 +576,6 @@ proc strTableAdd(t: var TStrTable, n: PSym) = strTableRawInsert(t.data, n) inc(t.counter) -proc reallySameIdent(a, b: string): bool {.inline.} = - when defined(nimfix): - result = a[0] == b[0] - else: - result = true - proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.discardable.} = # returns true if n is already in the string table: # It is essential that `n` is written nevertheless! @@ -594,7 +590,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.d # and overloading: (var x=@[]; x).mapIt(it). # So it is possible the very same sym is added multiple # times to the symbol table which we allow here with the 'it == n' check. - if it.name.id == n.name.id and reallySameIdent(it.name.s, n.name.s): + if it.name.id == n.name.id: if it == n: return false replaceSlot = h h = nextTry(h, high(t.data)) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b32f4b66b..e62956a0c 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -78,8 +78,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = [p.module.tmpBase, rope(id)]) else: result = makeCString(n.strVal) - of nkFloatLit..nkFloat64Lit: + of nkFloatLit, nkFloat64Lit: result = rope(n.floatVal.toStrMaxPrecision) + of nkFloat32Lit: + result = rope(n.floatVal.toStrMaxPrecision("f")) else: internalError(n.info, "genLiteral(" & $n.kind & ')') result = nil @@ -535,7 +537,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 @@ -575,16 +577,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 @@ -654,7 +657,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 @@ -2132,7 +2137,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if n.sons[0].kind != nkEmpty: genLineDir(p, n) var a: TLoc - initLocExpr(p, n.sons[0], a) + if n[0].kind in nkCallKinds: + # bug #6037: do not assign to a temp in C++ mode: + incl a.flags, lfSingleUse + genCall(p, n[0], a) + if lfSingleUse notin a.flags: + line(p, cpsStmts, a.r & ";" & tnl) + else: + initLocExpr(p, n.sons[0], a) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: @@ -2157,8 +2169,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): @@ -2214,7 +2225,7 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) = let field = obj.sym for i in 1..<cons.len: if cons[i].kind == nkExprColonExpr: - if cons[i][0].sym == field: + if cons[i][0].sym.name == field.name: result.add genConstExpr(p, cons[i][1]) return elif i == field.position: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 1bb26c48d..27bb89f60 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 f26854824..1c14d95f1 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 c9b047a49..d9e771ce7 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)) @@ -612,8 +610,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") @@ -898,14 +906,14 @@ proc genMainProc(m: BModule) = # prevents inlining of the NimMainInner function and dependent # functions, which might otherwise merge their stack frames. PreMainBody = - "void PreMainInner() {$N" & + "void PreMainInner(void) {$N" & "\tsystemInit000();$N" & "$1" & "$2" & "$3" & "}$N$N" & - "void PreMain() {$N" & - "\tvoid (*volatile inner)();$N" & + "void PreMain(void) {$N" & + "\tvoid (*volatile inner)(void);$N" & "\tsystemDatInit000();$N" & "\tinner = PreMainInner;$N" & "$4$5" & @@ -924,7 +932,7 @@ proc genMainProc(m: BModule) = NimMainProc = "N_CDECL(void, NimMain)(void) {$N" & - "\tvoid (*volatile inner)();$N" & + "\tvoid (*volatile inner)(void);$N" & "\tPreMain();$N" & "\tinner = NimMainInner;$N" & "$2" & diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 1165ec932..6f7d9f489 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -257,13 +257,14 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = cond = a else: cond = isn - var call = newNodeI(nkCall, base.info) + let retTyp = base.typ.sons[0] + let call = newNodeIT(nkCall, base.info, retTyp) addSon(call, newSymNode(curr)) for col in countup(1, paramLen - 1): addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), curr.typ.sons[col], false)) var ret: PNode - if base.typ.sons[0] != nil: + if retTyp != nil: var a = newNodeI(nkFastAsgn, base.info) addSon(a, newSymNode(base.ast.sons[resultPos].sym)) addSon(a, call) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 4303fd6c8..a4f47ac72 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -103,3 +103,4 @@ proc initDefines*() = defineSymbol("nimNewShiftOps") defineSymbol("nimDistros") defineSymbol("nimHasCppDefine") + defineSymbol("nimGenericInOutFlags") 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/installer.ini b/compiler/installer.ini index 0987aa6be..8cc89da9f 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -66,6 +66,8 @@ Files: "compiler" Files: "doc" Files: "doc/html" Files: "tools" +Files: "nimsuggest" +Files: "nimsuggest/tests/*.nim" Files: "web/website.ini" Files: "web/ticker.html" Files: "web/*.nim" diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ee35356c9..46766cfcf 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,31 @@ 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) + let it = n[i] + case it.kind + of nkStrLit..nkTripleStrLit: + p.body.add(it.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()") + let v = it.sym + # for backwards compatibility we don't deref syms here :-( + if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: + if p.target == targetPHP: p.body.add "$" + p.body.add mangleName(v, p.target) + else: + var r: TCompRes + gen(p, it, r) + p.body.add(r.rdLoc) + else: + var r: TCompRes + gen(p, it, r) + p.body.add(r.rdLoc) + p.body.add tnl proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes @@ -794,18 +840,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 @@ -834,6 +880,16 @@ proc generateHeader(p: PProc, typ: PType): Rope = # add(result, name) # add(result, "_Idx") +proc countJsParams(typ: PType): int = + for i in countup(1, sonsLen(typ.n) - 1): + assert(typ.n.sons[i].kind == nkSym) + var param = typ.n.sons[i].sym + if isCompileTimeOnly(param.typ): continue + if mapType(param.typ) == etyBaseIndex: + inc result, 2 + else: + inc result + const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, @@ -854,7 +910,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 +918,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 +932,31 @@ 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]) + elif b.typ == etyBaseIndex: + lineF(p, "$# = $#;$n", [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 +964,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 +983,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 @@ -992,15 +1059,15 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = var typ = skipTypes(m.sons[0].typ, abstractPtrs) if typ.kind == tyArray: first = firstOrd(typ.sons[0]) else: first = 0 - if optBoundsCheck in p.options and not isConstExpr(m.sons[1]): + if optBoundsCheck in p.options: useMagic(p, "chckIndx") if p.target == targetPHP: if typ.kind != tyString: - r.res = "chckIndx($1, $2, count($3))-$2" % [b.res, rope(first), a.res] + r.res = "chckIndx($1, $2, count($3)-1)-$2" % [b.res, rope(first), a.res] else: r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res] else: - r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res] + r.res = "chckIndx($1, $2, $3.length-1)-$2" % [b.res, rope(first), a.res] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: @@ -1193,17 +1260,21 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = - if mapType(p, n.sons[0].typ) == etyObject: - gen(p, n.sons[0], r) + let it = n.sons[0] + let t = mapType(p, it.typ) + if t == etyObject: + gen(p, it, r) else: var a: TCompRes - gen(p, n.sons[0], a) + gen(p, it, a) r.kind = resExpr if a.typ == etyBaseIndex: r.res = "$1[$2]" % [a.address, a.res] - elif n.sons[0].kind == nkCall: + elif it.kind == nkCall: let tmp = p.getTemp r.res = "($1 = $2, $1[0])[$1[1]]" % [tmp, a.res] + elif t == etyBaseIndex: + r.res = "$1[0]" % [a.res] else: internalError(n.info, "genDeref") @@ -1217,7 +1288,7 @@ proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = else: add(r.res, a.res) -proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) = +proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) = var a: TCompRes gen(p, n, a) if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and @@ -1227,6 +1298,12 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) = add(r.res, a.address) add(r.res, ", ") add(r.res, a.res) + if emitted != nil: inc emitted[] + elif n.typ.kind == tyVar and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: + # this fixes bug #5608: + let tmp = getTemp(p) + add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc]) + if emitted != nil: inc emitted[] else: add(r.res, a.res) @@ -1237,6 +1314,7 @@ proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) = var typ = skipTypes(n.sons[0].typ, abstractInst) assert(typ.kind == tyProc) assert(sonsLen(typ) == sonsLen(typ.n)) + var emitted = start-1 for i in countup(start, sonsLen(n) - 1): let it = n.sons[i] @@ -1250,9 +1328,16 @@ proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) = if paramType.isNil: genArgNoParam(p, it, r) else: - genArg(p, it, paramType.sym, r) + genArg(p, it, paramType.sym, r, addr emitted) + inc emitted hasArgs = true add(r.res, ")") + when false: + # XXX look into this: + let jsp = countJsParams(typ) + if emitted != jsp and tfVarargs notin typ.flags: + localError(n.info, "wrong number of parameters emitted; expected: " & $jsp & + " but got: " & $emitted) r.kind = resExpr proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType; @@ -1385,7 +1470,7 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) = var t = typ - if tfFinal notin t.flags or t.sons[0] != nil: + if objHasTypeField(t): if output.len > 0: output.add(", ") addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)]) while t != nil: @@ -1483,10 +1568,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 +1586,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 +1635,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 +1801,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 +1811,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 +1887,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)") @@ -1916,10 +2015,19 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = if i > 1: add(initList, ", ") var it = n.sons[i] internalAssert it.kind == nkExprColonExpr - gen(p, it.sons[1], a) + let val = it.sons[1] + gen(p, val, a) var f = it.sons[0].sym if f.loc.r == nil: f.loc.r = mangleName(f, p.target) fieldIDs.incl(f.id) + + let typ = val.typ.skipTypes(abstractInst) + if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and + mapType(p, typ) != etyBaseIndex) or needsNoCopy(p, it.sons[1]): + discard + else: + useMagic(p, "nimCopy") + a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) @@ -1986,15 +2094,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 +2115,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 +2129,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 +2150,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 +2340,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 +2471,4 @@ proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = result = r const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) + diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index ae30861e7..0d5b29ace 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -67,9 +67,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = rope(lengthOrd(field.typ)), makeJSString(field.name.s), result] else: internalError(n.info, "genObjectFields") +proc objHasTypeField(t: PType): bool {.inline.} = + tfInheritable in t.flags or t.sons[0] != nil + proc genObjectInfo(p: PProc, typ: PType, name: Rope) = + let kind = if objHasTypeField(typ): tyObject else: tyTuple var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " & - "finalizer: null};$n") % [name, rope(ord(typ.kind))] + "finalizer: null};$n") % [name, rope(ord(kind))] prepend(p.g.typeInfo, s) addf(p.g.typeInfo, "var NNI$1 = $2;$n", [rope(typ.id), genObjectFields(p, typ, typ.n)]) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index e0875a118..09bcb4ce0 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -386,11 +386,11 @@ proc getNumber(L: var TLexer, result: var TToken) = else: matchUnderscoreChars(L, result, {'0'..'9'}) if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): - result.tokType = tkFloat64Lit + result.tokType = tkFloatLit eatChar(L, result, '.') matchUnderscoreChars(L, result, {'0'..'9'}) if L.buf[L.bufpos] in {'e', 'E'}: - result.tokType = tkFloat64Lit + result.tokType = tkFloatLit eatChar(L, result, 'e') if L.buf[L.bufpos] in {'+', '-'}: eatChar(L, result) @@ -516,7 +516,8 @@ proc getNumber(L: var TLexer, result: var TToken) = result.fNumber = (cast[PFloat32](addr(xi)))[] # note: this code is endian neutral! # XXX: Test this on big endian machine! - of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[] + of tkFloat64Lit, tkFloatLit: + result.fNumber = (cast[PFloat64](addr(xi)))[] else: internalError(getLineInfo(L), "getNumber") # Bounds checks. Non decimal literals are allowed to overflow the range of diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 3a97f1ed2..5f4a0caf1 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -113,6 +113,7 @@ type errGenericLambdaNotAllowed, errProcHasNoConcreteType, errCompilerDoesntSupportTarget, + errInOutFlagNotExtern, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -380,6 +381,7 @@ const "of the generic paramers can be inferred from the expected signature.", errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", + errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\'", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", @@ -541,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 3e681c8d1..bf1b04c2c 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -187,6 +187,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 e2d17b455..3cd1e4d92 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -39,6 +39,9 @@ type inPragma*: int # Pragma level inSemiStmtList*: int + SymbolMode = enum + smNormal, smAllowNil, smAfterDot + proc parseAll*(p: var TParser): PNode proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode @@ -62,7 +65,7 @@ proc optPar*(p: var TParser) proc optInd*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode) proc setBaseFlags*(n: PNode, base: TNumericalBase) -proc parseSymbol*(p: var TParser, allowNil = false): PNode +proc parseSymbol*(p: var TParser, mode = smNormal): PNode proc parseTry(p: var TParser; isExpr: bool): PNode proc parseCase(p: var TParser): PNode proc parseStmtPragma(p: var TParser): PNode @@ -304,13 +307,24 @@ proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) skipComment(p, n) -proc parseSymbol(p: var TParser, allowNil = false): PNode = +proc parseSymbol(p: var TParser, mode = smNormal): PNode = #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' - #| | IDENT | 'addr' | 'type' + #| | IDENT | KEYW case p.tok.tokType - of tkSymbol, tkAddr, tkType: + of tkSymbol: result = newIdentNodeP(p.tok.ident, p) getTok(p) + of tokKeywordLow..tokKeywordHigh: + if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot: + # for backwards compatibility these 2 are always valid: + result = newIdentNodeP(p.tok.ident, p) + getTok(p) + elif p.tok.tokType == tkNil and mode == smAllowNil: + result = newNodeP(nkNilLit, p) + getTok(p) + else: + parMessage(p, errIdentifierExpected, p.tok) + result = ast.emptyNode of tkAccent: result = newNodeP(nkAccQuoted, p) getTok(p) @@ -336,16 +350,12 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode = break eat(p, tkAccent) else: - if allowNil and p.tok.tokType == tkNil: - result = newNodeP(nkNilLit, p) - getTok(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - # BUGFIX: We must consume a token here to prevent endless loops! - # But: this really sucks for idetools and keywords, so we don't do it - # if it is a keyword: - #if not isKeyword(p.tok.tokType): getTok(p) - result = ast.emptyNode + parMessage(p, errIdentifierExpected, p.tok) + # BUGFIX: We must consume a token here to prevent endless loops! + # But: this really sucks for idetools and keywords, so we don't do it + # if it is a keyword: + #if not isKeyword(p.tok.tokType): getTok(p) + result = ast.emptyNode proc colonOrEquals(p: var TParser, a: PNode): PNode = if p.tok.tokType == tkColon: @@ -390,7 +400,7 @@ proc dotExpr(p: var TParser, a: PNode): PNode = result = newNodeI(nkDotExpr, info) optInd(p, result) addSon(result, a) - addSon(result, parseSymbol(p)) + addSon(result, parseSymbol(p, smAfterDot)) proc qualifiedIdent(p: var TParser): PNode = #| qualifiedIdent = symbol ('.' optInd symbol)? @@ -846,6 +856,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 +870,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 @@ -1004,10 +1015,10 @@ proc isExprStart(p: TParser): bool = result = true else: result = false -proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = +proc parseSymbolList(p: var TParser, result: PNode) = # progress guaranteed while true: - var s = parseSymbol(p, allowNil) + var s = parseSymbol(p, smAllowNil) if s.kind == nkEmpty: break addSon(result, s) if p.tok.tokType != tkComma: break @@ -1028,7 +1039,7 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, getTok(p) let list = newNodeP(nodeKind, p) result.addSon list - parseSymbolList(p, list, allowNil = true) + parseSymbolList(p, list) proc parseExpr(p: var TParser): PNode = #| expr = (ifExpr @@ -1520,6 +1531,13 @@ proc parseGenericParam(p: var TParser): PNode = # progress guaranteed while true: case p.tok.tokType + of tkIn, tkOut: + let x = p.lex.cache.getIdent(if p.tok.tokType == tkIn: "in" else: "out") + a = newNodeP(nkPrefix, p) + a.addSon newIdentNodeP(x, p) + getTok(p) + expectIdent(p) + a.addSon(parseSymbol(p)) of tkSymbol, tkAccent: a = parseSymbol(p) if a.kind == nkEmpty: return @@ -1548,7 +1566,7 @@ proc parseGenericParamList(p: var TParser): PNode = getTok(p) optInd(p, result) # progress guaranteed - while p.tok.tokType in {tkSymbol, tkAccent}: + while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}: var a = parseGenericParam(p) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break @@ -1882,7 +1900,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) @@ -1898,7 +1916,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/pbraces.nim b/compiler/pbraces.nim index b59fbc6cf..fa4ccc139 100644 --- a/compiler/pbraces.nim +++ b/compiler/pbraces.nim @@ -1336,6 +1336,11 @@ proc parseGenericParam(p: var TParser): PNode = result = newNodeP(nkIdentDefs, p) while true: case p.tok.tokType + of tkIn, tkOut: + let t = p.tok.tokType + getTok(p) + expectIdent(p) + a = parseSymbol(p) of tkSymbol, tkAccent: a = parseSymbol(p) if a.kind == nkEmpty: return diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b30b94b5d..7e1db5b29 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -125,8 +125,9 @@ proc processImportCpp(s: PSym, extname: string, info: TLineInfo) = incl(s.flags, sfImportc) incl(s.flags, sfInfixCall) excl(s.flags, sfForward) - let m = s.getModule() - incl(m.flags, sfCompileToCpp) + if gCmd == cmdCompileToC: + let m = s.getModule() + incl(m.flags, sfCompileToCpp) extccomp.gMixedMode = true proc processImportObjC(s: PSym, extname: string, info: TLineInfo) = diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index d24e8f560..77f7c844f 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -12,17 +12,17 @@ import strutils proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} -proc toStrMaxPrecision*(f: BiggestFloat): string = +proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = if f != f: result = "NAN" elif f == 0.0: - result = "0.0" + result = "0.0" & literalPostfix elif f == 0.5 * f: if f > 0.0: result = "INF" else: result = "-INF" else: var buf: array[0..80, char] - c_sprintf(buf, "%#.16e", f) + c_sprintf(buf, "%#.16e" & literalPostfix, f) result = $buf proc encodeStr*(s: string, result: var string) = 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 1089ab7db..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, @@ -429,7 +435,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = for i in 1..sonsLen(n)-1: let formal = s.ast.sons[genericParamsPos].sons[i-1].typ let arg = n[i].typ - let tm = typeRel(m, formal, arg, true) + let tm = typeRel(m, formal, arg) if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved @@ -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 8f2c802de..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,6 +282,10 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = assert n != nil result.n = n +proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]): PType = + result = newType(kind, owner) + result.sons = sons + proc newTypeWithSons*(c: PContext, kind: TTypeKind, sons: seq[PType]): PType = result = newType(kind, getCurrOwner(c)) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8ff5fdd9c..76d4be766 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])) @@ -683,27 +691,9 @@ proc bracketedMacro(n: PNode): PSym = if result.kind notin {skMacro, skTemplate}: result = nil -proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym; - flags: TExprFlags): PNode = - # We received untransformed bracket expression coming from macroOrTmpl[]. - # Transform it to macro or template call, where first come normal - # arguments, next come generic template arguments. - var sons = newSeq[PNode]() - sons.add inner.sons[0] - # Normal arguments: - for i in 1..<outer.len: - sons.add outer.sons[i] - # Generic template arguments from bracket expression: - for i in 1..<inner.len: - sons.add inner.sons[i] - shallowCopy(outer.sons, sons) - # FIXME: Shouldn't we check sfImmediate and call semDirectOp? - # However passing to semDirectOp doesn't work here. - case s.kind - of skMacro: result = semMacroExpr(c, outer, outer, s, flags) - of skTemplate: result = semTemplateExpr(c, outer, s, flags) - else: assert(false) - return +proc setGenericParams(c: PContext, n: PNode) = + for i in 1 .. <n.len: + n[i].typ = semTypeNode(c, n[i], nil) proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = result = n @@ -718,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 = @@ -745,7 +735,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = elif n.sons[0].kind == nkBracketExpr: let s = bracketedMacro(n.sons[0]) if s != nil: - return semBracketedMacro(c, n, n.sons[0], s, flags) + setGenericParams(c, n[0]) + return semDirectOp(c, n, flags) let nOrig = n.copyTree semOpAux(c, n) @@ -897,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} @@ -937,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 @@ -997,7 +974,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = of skParam: markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: + if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil: # XXX see the hack in sigmatch.nim ... return s.typ.n elif sfGenSym in s.flags: @@ -1155,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: @@ -2164,10 +2141,6 @@ proc shouldBeBracketExpr(n: PNode): bool = n.sons[0] = be return true -proc setGenericParams(c: PContext, n: PNode) = - for i in 1 .. <n.len: - n[i].typ = semTypeNode(c, n[i], nil) - proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -2177,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) @@ -2214,11 +2187,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.typ == nil: result.typ = getSysType(tyUInt32) of nkUInt64Lit: if result.typ == nil: result.typ = getSysType(tyUInt64) - of nkFloatLit: - if result.typ == nil: result.typ = getFloatLitType(result) + #of nkFloatLit: + # if result.typ == nil: result.typ = getFloatLitType(result) of nkFloat32Lit: if result.typ == nil: result.typ = getSysType(tyFloat32) - of nkFloat64Lit: + of nkFloat64Lit, nkFloatLit: if result.typ == nil: result.typ = getSysType(tyFloat64) of nkFloat128Lit: if result.typ == nil: result.typ = getSysType(tyFloat128) @@ -2306,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 3e1989eaf..c664f735c 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -89,9 +89,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode = proc toNode(t: PType, i: TLineInfo): PNode = result = newNodeIT(nkType, i, t) -const +const # these are types that use the bracket syntax for instantiation - # they can be subjected to the type traits `genericHead` and + # they can be subjected to the type traits `genericHead` and # `Uninstantiated` tyUserDefinedGenerics* = {tyGenericInst, tyGenericInvocation, tyUserTypeClassInst} @@ -109,27 +109,43 @@ proc uninstantiate(t: PType): PType = of tyCompositeTypeClass: uninstantiate t.sons[1] else: t -proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = - var typ = operand.skipTypes({tyTypeDesc}) +proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = + const skippedTypes = {tyTypeDesc, tyAlias} + let trait = traitCall[0] + internalAssert trait.kind == nkSym + var operand = operand.skipTypes(skippedTypes) + + template operand2: PType = + traitCall.sons[2].typ.skipTypes({tyTypeDesc}) + + template typeWithSonsResult(kind, sons): PNode = + newTypeWithSons(context, kind, sons).toNode(traitCall.info) + case trait.sym.name.s + of "or", "|": + return typeWithSonsResult(tyOr, @[operand, operand2]) + of "and": + return typeWithSonsResult(tyAnd, @[operand, operand2]) + of "not": + return typeWithSonsResult(tyNot, @[operand]) of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferName)) + result = newStrNode(nkStrLit, operand.typeToString(preferName)) result.typ = newType(tyString, context) - result.info = trait.info + result.info = traitCall.info of "arity": - result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc)) + result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc)) result.typ = newType(tyInt, context) - result.info = trait.info + result.info = traitCall.info of "genericHead": - var res = uninstantiate(typ) - if res == typ and res.kind notin tyMagicGenerics: - localError(trait.info, + var res = uninstantiate(operand) + if res == operand and res.kind notin tyMagicGenerics: + localError(traitCall.info, "genericHead expects a generic type. The given type was " & - typeToString(typ)) - return newType(tyError, context).toNode(trait.info) - result = res.base.toNode(trait.info) + typeToString(operand)) + return newType(tyError, context).toNode(traitCall.info) + result = res.base.toNode(traitCall.info) of "stripGenericParams": - result = uninstantiate(typ).toNode(trait.info) + result = uninstantiate(operand).toNode(traitCall.info) else: internalAssert false @@ -140,7 +156,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode = if t.sonsLen > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) - result = evalTypeTrait(n[0], t, getCurrOwner(c)) + result = evalTypeTrait(n, t, getCurrOwner(c)) else: # a typedesc variable, pass unmodified to evals result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 96bdc6cba..e24c5fd29 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -69,7 +69,8 @@ proc `==`(a, b: TLockLevel): bool {.borrow.} proc max(a, b: TLockLevel): TLockLevel {.borrow.} proc isLocalVar(a: PEffects, s: PSym): bool = - s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner + s.kind in {skVar, skResult} and sfGlobal notin s.flags and + s.owner == a.owner and s.typ != nil proc getLockLevel(t: PType): TLockLevel = var t = t @@ -218,12 +219,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 e52a1b5cc..dbdb543f5 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,47 @@ 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 setVarType(v: PSym, typ: PType) = + if v.typ != nil and not sameTypeOrNil(v.typ, typ): + localError(v.info, "inconsistent typing for reintroduced symbol '" & + v.name.s & "': previous type was: " & typeToString(v.typ) & + "; new type is: " & typeToString(typ)) + v.typ = typ + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) @@ -529,6 +570,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) @@ -549,7 +595,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # this is needed for the evaluation pass and for the guard checking: v.ast = def if sfThread in v.flags: localError(def.info, errThreadvarCannotInit) - v.typ = typ + setVarType(v, typ) b = newNodeI(nkIdentDefs, a.info) if importantComments(): # keep documentation information: @@ -560,7 +606,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addToVarSection(c, result, n, b) else: if def.kind == nkPar: v.ast = def[j] - v.typ = tup.sons[j] + setVarType(v, tup.sons[j]) b.sons[j] = newSymNode(v) addDefer(c, result, v) checkNilable(v) @@ -594,7 +640,7 @@ proc semConst(c: PContext, n: PNode): PNode = if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: localError(a.info, "invalid type for const: " & typeToString(typ)) continue - v.typ = typ + setVarType(v, typ) v.ast = def # no need to copy if sfGenSym notin v.flags: addInterfaceDecl(c, v) var b = newNodeI(nkConstDef, a.info) @@ -730,7 +776,9 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = var s: PSym if name.kind == nkDotExpr: s = qualifiedLookUp(c, name, {checkUndeclared, checkModule}) - if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags: + if s.kind != skType or + s.typ.skipTypes(abstractPtrs).kind != tyObject or + tfPartial notin s.typ.skipTypes(abstractPtrs).flags: localError(name.info, "only .partial objects can be extended") else: s = semIdentDef(c, name, skType) @@ -742,6 +790,87 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = if sfGenSym notin s.flags: addInterfaceDecl(c, s) a.sons[0] = newSymNode(s) +proc checkCovariantParamsUsages(genericType: PType) = + var body = genericType{-1} + + proc traverseSubTypes(t: PType): bool = + template error(msg) = localError(genericType.sym.info, msg) + + result = false + + template subresult(r) = + let sub = r + result = result or sub + + case t.kind + of tyGenericParam: + t.flags.incl tfWeakCovariant + return true + + of tyObject: + for field in t.n: + subresult traverseSubTypes(field.typ) + + of tyArray: + return traverseSubTypes(t[1]) + + of tyProc: + for subType in t.sons: + if subType != nil: + subresult traverseSubTypes(subType) + if result: + error("non-invariant type param used in a proc type: " & $t) + + of tySequence: + return traverseSubTypes(t[0]) + + of tyGenericInvocation: + let targetBody = t[0] + for i in 1 .. <t.len: + let param = t[i] + if param.kind == tyGenericParam: + if tfCovariant in param.flags: + let formalFlags = targetBody[i-1].flags + if tfCovariant notin formalFlags: + error("covariant param '" & param.sym.name.s & + "' used in a non-covariant position") + elif tfWeakCovariant in formalFlags: + param.flags.incl tfWeakCovariant + result = true + elif tfContravariant in param.flags: + let formalParam = targetBody[i-1].sym + if tfContravariant notin formalParam.typ.flags: + error("contravariant param '" & param.sym.name.s & + "' used in a non-contravariant position") + result = true + else: + subresult traverseSubTypes(param) + + of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass: + error("non-invariant type parameters cannot be used with types such '" & $t & "'") + + of tyUserTypeClass, tyUserTypeClassInst: + error("non-invariant type parameters are not supported in concepts") + + of tyTuple: + for fieldType in t.sons: + subresult traverseSubTypes(fieldType) + + of tyPtr, tyRef, tyVar: + if t.base.kind == tyGenericParam: return true + return traverseSubTypes(t.base) + + of tyDistinct, tyAlias: + return traverseSubTypes(t.lastSon) + + of tyGenericInst: + internalAssert false + + else: + discard + + discard traverseSubTypes(body) + proc typeSectionRightSidePass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] @@ -782,6 +911,22 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = body.sym = s body.size = -1 # could not be computed properly s.typ.sons[sonsLen(s.typ) - 1] = body + if tfCovariant in s.typ.flags: + checkCovariantParamsUsages(s.typ) + # XXX: This is a temporary limitation: + # The codegen currently produces various failures with + # generic imported types that have fields, but we need + # the fields specified in order to detect weak covariance. + # The proper solution is to teach the codegen how to handle + # such types, because this would offer various interesting + # possibilities such as instantiating C++ generic types with + # garbage collected Nim types. + if sfImportc in s.flags: + var body = s.typ.lastSon + if body.kind == tyObject: + # erases all declared fields + body.n.sons = nil + popOwner(c) closeScope(c) elif a.sons[2].kind != nkEmpty: @@ -985,12 +1130,15 @@ proc semProcAnnotation(c: PContext, prc: PNode; var x = newNodeI(nkCall, n.info) x.add(newSymNode(m)) prc.sons[pragmasPos] = copyExcept(n, i) + if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: + prc.sons[pragmasPos] = emptyNode + if it.kind == nkExprColonExpr: # pass pragma argument to the macro too: 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 @@ -1322,8 +1470,16 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, implicitPragmas(c, s, n, validPragmas) else: if n.sons[pragmasPos].kind != nkEmpty: - localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX, - "'" & proto.name.s & "' from " & $proto.info) + pragma(c, s, n.sons[pragmasPos], validPragmas) + # To ease macro generation that produce forwarded .async procs we now + # allow a bit redudancy in the pragma declarations. The rule is + # a prototype's pragma list must be a superset of the current pragma + # list. + # XXX This needs more checks eventually, for example that external + # linking names do agree: + if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: + localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX, + "'" & proto.name.s & "' from " & $proto.info) if sfForward notin proto.flags: wrongRedefinition(n.info, proto.name.s) excl(proto.flags, sfForward) @@ -1402,7 +1558,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popOwner(c) if n.sons[patternPos].kind != nkEmpty: c.patterns.add(s) - if isAnon: result.typ = s.typ + if isAnon: + n.kind = nkLambda + result.typ = s.typ if isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure: localError(s.info, "'.closure' calling convention for top level routines is invalid") @@ -1614,7 +1772,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 @@ -1629,7 +1788,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]): @@ -1653,7 +1812,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 16066da91..de71f1632 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -42,6 +42,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = counter = lastOrd(base) + 1 rawAddSon(result, base) let isPure = result.sym != nil and sfPure in result.sym.flags + var symbols: TStrTable + if isPure: initStrTable(symbols) var hasNull = false for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind @@ -87,6 +89,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = addSon(result.n, newSymNode(e)) styleCheckDef(e) if sfGenSym notin e.flags and not isPure: addDecl(c, e) + if isPure and strTableIncl(symbols, e): + wrongRedefinition(e.info, e.name.s) inc(counter) if not hasNull: incl(result.flags, tfNeedsInit) @@ -152,6 +156,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 +1135,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 +1206,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 +1419,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: @@ -1584,10 +1627,24 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = # type for each generic param. the index # of the parameter will be stored in the # attached symbol. + var paramName = a.sons[j] + var covarianceFlag = tfUnresolved + + if paramName.safeLen == 2: + if not nimEnableCovariance or paramName[0].ident.s == "in": + if father == nil or sfImportc notin father.sym.flags: + localError(paramName.info, errInOutFlagNotExtern, paramName[0].ident.s) + covarianceFlag = if paramName[0].ident.s == "in": tfContravariant + else: tfCovariant + if father != nil: father.flags.incl tfCovariant + paramName = paramName[1] + var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: - newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) + newSymG(skGenericParam, paramName, c).linkTo(finalType) else: - newSymG(skType, a.sons[j], c).linkTo(finalType) + newSymG(skType, paramName, c).linkTo(finalType) + + if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag) if def.kind != nkEmpty: s.ast = def if father != nil: addSonSkipIntLit(father, s.typ) s.position = result.len 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 d6a0c6382..6084e11c0 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -69,6 +69,12 @@ type mutabilityProblem*: uint8 # tyVar mismatch inheritancePenalty: int # to prefer closest father object type + TTypeRelFlag* = enum + trDontBind + trNoCovariance + + TTypeRelFlags* = set[TTypeRelFlag] + TTypeRelation* = enum # order is important! isNone, isConvertible, isIntConv, @@ -198,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, @@ -296,7 +304,33 @@ 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, doBind = true): 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 of tyNil: @@ -594,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)] @@ -617,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) @@ -650,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] @@ -818,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 @@ -860,7 +880,28 @@ template subtypeCheck() = if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: result = isNone -proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = +proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = + # this proc is always called for a pair of matching types + assert f.kind == a.kind + + template baseTypesCheck(lhs, rhs: PType): bool = + lhs.kind notin {tyPtr, tyRef, tyVar} and + typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype + + case f.kind + of tyRef, tyPtr: + return baseTypesCheck(f.base, a.base) + of tyGenericInst: + let body = f.base + return body == a.base and + a.sonsLen == 3 and + tfWeakCovariant notin body.sons[0].flags and + baseTypesCheck(f.sons[1], a.sons[1]) + else: + return false + +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 @@ -887,7 +928,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = 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 @@ -927,6 +968,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isEqual return + template doBind: bool = trDontBind notin flags + # var and static arguments match regular modifier-free types var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) # XXX: Theoretically, maybeSkipDistinct could be called before we even @@ -934,7 +977,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # 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: @@ -956,23 +1002,28 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = case a.kind of tyOr: + # XXX: deal with the current dual meaning of tyGenericParam + c.typedescMatched = true # seq[int|string] vs seq[number] # both int and string must match against number # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): result = isGeneric for branch in a.sons: - let x = typeRel(c, f, branch, false) + let x = typeRel(c, f, branch, flags + {trDontBind}) if x == isNone: return isNone if x < result: result = x + return of tyAnd: + # XXX: deal with the current dual meaning of tyGenericParam + c.typedescMatched = true # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough for branch in a.sons: - let x = typeRel(c, f, branch, false) + let x = typeRel(c, f, branch, flags + {trDontBind}) if x != isNone: return if x >= isGeneric: isGeneric else: x - result = isNone + return isNone of tyNot: case f.kind @@ -994,11 +1045,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = 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, false) - 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 @@ -1042,7 +1094,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyFloat128: result = handleFloatRange(f, a) of tyVar: if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) - else: result = typeRel(c, f.base, aOrig) + else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) subtypeCheck() of tyArray: case a.kind @@ -1056,13 +1108,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = fRange = a else: fRange = prev - result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}), - a.sons[1].skipTypes({tyTypeDesc})) - if result < isGeneric: return isNone + let ff = f.sons[1].skipTypes({tyTypeDesc}) + let aa = a.sons[1].skipTypes({tyTypeDesc}) + result = typeRel(c, ff, aa) + if result < isGeneric: + if nimEnableCovariance and + trNoCovariance notin flags and + ff.kind == aa.kind and + isCovariantPtr(c, ff, aa): + result = isSubtype + else: + return isNone 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): @@ -1077,20 +1137,31 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if tfOldSchoolExprStmt in f.sons[0].flags: if f.sons[0].kind == tyExpr: return elif f.sons[0].kind == tyStmt: return + + template matchArrayOrSeq(aBase: PType) = + let ff = f.base + let aa = aBase + let baseRel = typeRel(c, ff, aa) + if baseRel >= isGeneric: + result = isConvertible + elif nimEnableCovariance and + trNoCovariance notin flags and + ff.kind == aa.kind and + isCovariantPtr(c, ff, aa): + result = isConvertible + case a.kind of tyOpenArray, tyVarargs: result = typeRel(c, base(f), base(a)) if result < isGeneric: result = isNone of tyArray: if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): - result = isSubtype - elif typeRel(c, base(f), a.sons[1]) >= isGeneric: - result = isConvertible + return isSubtype + matchArrayOrSeq(a.sons[1]) of tySequence: if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): - result = isConvertible - elif typeRel(c, base(f), a.sons[0]) >= isGeneric: - result = isConvertible + return isConvertible + matchArrayOrSeq(a.sons[0]) of tyString: if f.kind == tyOpenArray: if f.sons[0].kind == tyChar: @@ -1105,8 +1176,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): result = isSubtype else: - result = typeRel(c, f.sons[0], a.sons[0]) - if result < isGeneric: result = isNone + let ff = f.sons[0] + let aa = a.sons[0] + result = typeRel(c, ff, aa) + if result < isGeneric: + if nimEnableCovariance and + trNoCovariance notin flags and + ff.kind == aa.kind and + isCovariantPtr(c, ff, aa): + result = isSubtype + else: + result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: result = isNilConversion of tyNil: result = f.allowsNil @@ -1158,7 +1238,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if a.len < f.len: return isNone for i in 0..f.len-2: if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone - result = typeRel(c, f.lastSon, a.lastSon) + result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance}) subtypeCheck() if result <= isConvertible: result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: @@ -1230,13 +1310,28 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = var m = c if a.kind == tyGenericInst: if roota.base == rootf.base: + let nextFlags = flags + {trNoCovariance} + var hasCovariance = false for i in 1 .. rootf.sonsLen-2: let ff = rootf.sons[i] let aa = roota.sons[i] - result = typeRel(c, ff, aa) - if result notin {isEqual, isGeneric}: return isNone - # if ff.kind == tyRange and result != isEqual: return isNone + result = typeRel(c, ff, aa, nextFlags) + if result notin {isEqual, isGeneric}: + if trNoCovariance notin flags and ff.kind == aa.kind: + let paramFlags = rootf.base.sons[i-1].flags + hasCovariance = + if tfCovariant in paramFlags: + if tfWeakCovariant in paramFlags: + isCovariantPtr(c, ff, aa) + else: + ff.kind notin {tyRef, tyPtr} and result == isSubtype + else: + tfContravariant in paramFlags and + typeRel(c, aa, ff) == isSubtype + if hasCovariance: + continue + return isNone if prev == nil: put(c, f, a) result = isGeneric else: @@ -1260,7 +1355,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = 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: @@ -1396,7 +1492,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = 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) @@ -1450,7 +1547,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone else: if f.sonsLen > 0 and f.sons[0].kind != tyNone: - result = typeRel(c, f.lastSon, a, false) + result = typeRel(c, f.lastSon, a, flags + {trDontBind}) if doBind and result notin {isNone, isGeneric}: let concrete = concreteType(c, a) if concrete == nil: return isNone @@ -1488,6 +1585,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = 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: @@ -1691,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) @@ -2140,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/typesrenderer.nim b/compiler/typesrenderer.nim index e9c27ac9d..74eb00f4e 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -52,13 +52,16 @@ proc renderType(n: PNode): string = else: result = "ptr" of nkProcTy: - assert len(n) > 1 - let params = n[0] - assert params.kind == nkFormalParams - assert len(params) > 0 - result = "proc(" - for i in 1 .. <len(params): result.add(renderType(params[i]) & ',') - result[<len(result)] = ')' + assert len(n) != 1 + if len(n) > 1: + let params = n[0] + assert params.kind == nkFormalParams + assert len(params) > 0 + result = "proc(" + for i in 1 .. <len(params): result.add(renderType(params[i]) & ',') + result[<len(result)] = ')' + else: + result = "proc" of nkIdentDefs: assert len(n) >= 3 let typePos = len(n) - 2 diff --git a/compiler/vm.nim b/compiler/vm.nim index 043506e62..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: @@ -1609,8 +1611,9 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg = iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = let gp = macroSym.ast[genericParamsPos] for i in 0 .. <gp.len: - let idx = macroSym.typ.len + i - yield (gp[i].sym, call.sons[idx]) + let genericParam = gp[i].sym + let posInCall = macroSym.typ.len + i + yield (genericParam, call[posInCall]) var evalMacroCounter: int 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/advopt.txt b/doc/advopt.txt index 9a9dab7e0..b88e5f063 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -16,7 +16,7 @@ Advanced commands: //check checks the project for syntax and semantic Advanced options: - -o, --out:FILE set the output filename + -o:FILE, --out:FILE set the output filename --stdout output to stdout --colors:on|off turn compiler messages coloring on|off --listFullPaths list full paths in messages 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/grammar.txt b/doc/grammar.txt index f50045233..58b119331 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -22,7 +22,7 @@ plusExpr = mulExpr (OP8 optInd mulExpr)* mulExpr = dollarExpr (OP9 optInd dollarExpr)* dollarExpr = primary (OP10 optInd primary)* symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' - | IDENT | 'addr' | 'type' + | IDENT | KEYW exprColonEqExpr = expr (':'|'=' expr)? exprList = expr ^+ comma dotExpr = expr '.' optInd symbol @@ -81,7 +81,6 @@ paramList = '(' declColonEquals ^* (comma/semicolon) ')' paramListArrow = paramList? ('->' optInd typeDesc)? paramListColon = paramList? (':' optInd typeDesc)? doBlock = 'do' paramListArrow pragmas? colcom stmt -doBlocks = doBlock ^* IND{=} procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? distinct = 'distinct' optInd typeDesc expr = (ifExpr @@ -98,10 +97,11 @@ primary = typeKeyw typeDescK typeDesc = simpleExpr typeDefAux = simpleExpr | 'concept' typeClass -macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt - | IND{=} 'elif' expr ':' stmt - | IND{=} 'except' exprList ':' stmt - | IND{=} 'else' ':' stmt )* +postExprBlocks = ':' stmt? ( IND{=} doBlock + | IND{=} 'of' exprList ':' stmt + | IND{=} 'elif' expr ':' stmt + | IND{=} 'except' exprList ':' stmt + | IND{=} 'else' ':' stmt )* exprStmt = simpleExpr (( '=' optInd expr colonBody? ) / ( expr ^+ comma diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index d30c37ff7..bd90cd73d 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -1006,16 +1006,37 @@ CodegenDecl pragma ------------------ The ``codegenDecl`` pragma can be used to directly influence Nim's code -generator. It receives a format string that determines how the variable or -proc is declared in the generated code: +generator. It receives a format string that determines how the variable +or proc is declared in the generated code. + +For variables $1 in the format string represents the type of the variable +and $2 is the name of the variable. + +The following Nim code: .. code-block:: nim var a {.codegenDecl: "$# progmem $#".}: int + +will generate this C code: + +.. code-block:: c + int progmem a + +For procedures $1 is the return type of the procedure, $2 is the name of +the procedure and $3 is the parameter list. +The following nim code: + +.. code-block:: nim proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = echo "realistic interrupt handler" +will generate this code: + +.. code-block:: c + __interrupt void myinterrupt() + InjectStmt pragma ----------------- diff --git a/doc/manual/type_rel.txt b/doc/manual/type_rel.txt index 5b68f73aa..1d1425934 100644 --- a/doc/manual/type_rel.txt +++ b/doc/manual/type_rel.txt @@ -111,6 +111,105 @@ relation is extended to the types ``var``, ``ref``, ``ptr``: .. XXX nil is a special value! +Covariance +---------- + +Covariance in Nim can be introduced only though pointer-like types such +as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated +with pointer-like types will be considered covariant if and only if they +are also immutable. The introduction of a ``var`` modifier or additional +``ptr`` or ``ref`` indirections would result in invariant treatment of +these types. + +``proc`` types are currently always invariant, but future versions of Nim +may relax this rule. + +User-defined generic types may also be covariant with respect to some of +their parameters. By default, all generic params are considered invariant, +but you may choose the apply the prefix modifier ``in`` to a parameter to +make it contravariant or ``out`` to make it covariant: + +.. code-block:: nim + type + AnnotatedPtr[out T] = + metadata: MyTypeInfo + p: ref T + + RingBuffer[out T] = + startPos: int + data: seq[T] + + Action {.importcpp: "std::function<void ('0)>".} [in T] = object + +When the designated generic parameter is used to instantiate a pointer-like +type as in the case of `AnnotatedPtr` above, the resulting generic type will +also have pointer-like covariance: + +.. code-block:: nim + type + GuiWidget = object of RootObj + Button = object of GuiWidget + ComboBox = object of GuiWidget + + var + widgetPtr: AnnotatedPtr[GuiWidget] + buttonPtr: AnnotatedPtr[Button] + + ... + + proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ... + + # you can call procs expecting base types by supplying a derived type + drawWidget(buttonPtr) + + # and you can convert more-specific pointer types to more general ones + widgetPtr = buttonPtr + +Just like with regular pointers, covariance will be enabled only for immutable +values: + +.. code-block:: nim + proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) = + x.p = new(ComboBox) + + makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified + # to point to a ComboBox + +On the other hand, in the `RingBuffer` example above, the designated generic +param is used to instantiate the non-pointer ``seq`` type, which means that +the resulting generic type will have covariance that mimics an array or +sequence (i.e. it will be covariant only when instantiated with ``ptr`` and +``ref`` types): + +.. code-block:: nim + + type + Base = object of RootObj + Derived = object of Base + + proc consumeBaseValues(b: RingBuffer[Base]) = ... + + var derivedValues: RingBuffer[Derived] + + consumeBaseValues(derivedValues) # Error, Base and Derived values may differ + # in size + + proc consumeBasePointers(b: RingBuffer[ptr Base]) = ... + + var derivedPointers: RingBuffer[ptr Derived] + + consumeBaseValues(derivedPointers) # This is legal + +Please note that Nim will treat the user-defined pointer-like types as +proper alternatives to the built-in pointer types. That is, types such +as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be +considered covariant and you can create new pointer-like types by instantiating +other user-defined pointer-like types. + +The contravariant parameters introduced with the ``in`` modifier are currently +useful only when interfacing with imported types having such semantics. + + Convertible relation -------------------- A type ``a`` is **implicitly** convertible to type ``b`` iff the following @@ -119,6 +218,8 @@ algorithm returns true: .. code-block:: nim # XXX range types? proc isImplicitlyConvertible(a, b: PType): bool = + if isSubtype(a, b) or isCovariant(a, b): + return true case a.kind of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float, float32, float64} 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/doc/tut1.rst b/doc/tut1.rst index 436b3880d..fc8e411cb 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1492,20 +1492,6 @@ variables! For example: echo badname echo badext -Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code -won't compile: - -.. code-block:: nim - - import os - - var - path = "usr/local/nimc.html" - dir, name, ext = "" - - (dir, name, ext) = splitFile(path) - # --> Error: '(dir, name, ext)' cannot be assigned to - Reference and pointer types --------------------------- diff --git a/doc/tut2.rst b/doc/tut2.rst index 985a7257f..f145528a1 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -206,7 +206,7 @@ for any type: echo "abc".len # is the same as echo len("abc") echo "abc".toUpper() - echo {'a', 'b', 'c'}.card + echo({'a', 'b', 'c'}.card) stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo") (Another way to look at the method call syntax is that it provides the missing diff --git a/koch.nim b/koch.nim index da99ea264..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,48 +392,25 @@ 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 # determine which mingw link to put in the NSIS installer. nimexec "c --out:koch_temp --cpu:$# koch" % cpu exec "koch_temp boot -d:release --cpu:$#" % cpu - exec "koch_temp nsis -d:release" exec "koch_temp zip -d:release" - - when false: - # we now disable the NSIS installer as it cannot download from https - # and is broken in so many different ways it's not funny anymore: - moveFile r"build\nim_$#.exe" % VersionAsString, - r"web\upload\download\nim-$#_x$#.exe" % [VersionAsString, arch] - moveFile r"build\nim-$#.zip" % VersionAsString, + overwriteFile r"build\nim-$#.zip" % VersionAsString, r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch] proc winRelease() = @@ -438,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: @@ -489,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" @@ -564,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 83776f16b..af1e9de28 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -225,7 +225,13 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.} proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.} proc newNimNode*(kind: NimNodeKind, - n: NimNode=nil): NimNode {.magic: "NNewNimNode", noSideEffect.} + lineInfoFrom: NimNode=nil): NimNode + {.magic: "NNewNimNode", noSideEffect.} + ## Creates a new AST node of the specified kind. + ## + ## The ``lineInfoFrom`` parameter is used for line information when the + ## produced code crashes. You should ensure that it is set to a node that + ## you are transforming. proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.} proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} @@ -249,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) @@ -269,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 @@ -426,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) @@ -744,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/posix/posix.nim b/lib/posix/posix.nim index 02312a4d5..55d1dd2eb 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -79,6 +79,9 @@ const DT_SOCK* = 12 ## UNIX domain socket. DT_WHT* = 14 +# Special types +type Sighandler = proc (a: cint) {.noconv.} + # Platform specific stuff when defined(linux) and defined(amd64): @@ -86,6 +89,9 @@ when defined(linux) and defined(amd64): else: include posix_other +# There used to be this name in posix.nim a long time ago, not sure why! +{.deprecated: [cSIG_HOLD: SIG_HOLD].} + when not defined(macosx): proc st_atime*(s: Stat): Time {.inline.} = ## Second-granularity time of last access @@ -659,7 +665,7 @@ proc sighold*(a1: cint): cint {.importc, header: "<signal.h>".} proc sigignore*(a1: cint): cint {.importc, header: "<signal.h>".} proc siginterrupt*(a1, a2: cint): cint {.importc, header: "<signal.h>".} proc sigismember*(a1: var Sigset, a2: cint): cint {.importc, header: "<signal.h>".} -proc signal*(a1: cint, a2: proc (x: cint) {.noconv.}) {. +proc signal*(a1: cint, a2: Sighandler) {. importc, header: "<signal.h>".} proc sigpause*(a1: cint): cint {.importc, header: "<signal.h>".} proc sigpending*(a1: var Sigset): cint {.importc, header: "<signal.h>".} diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 70f7e710f..c44128b16 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -36,6 +36,9 @@ type {.deprecated: [TSocketHandle: SocketHandle].} +# not detected by detect.nim, guarded by #ifdef __USE_UNIX98 in glibc +const SIG_HOLD* = cast[SigHandler](2) + type Timespec* {.importc: "struct timespec", header: "<time.h>", final, pure.} = object ## struct timespec diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim index 9e2ed32e1..4b693960e 100644 --- a/lib/posix/posix_linux_amd64_consts.nim +++ b/lib/posix/posix_linux_amd64_consts.nim @@ -399,6 +399,9 @@ const SS_ONSTACK* = cint(1) const SS_DISABLE* = cint(2) const MINSIGSTKSZ* = cint(2048) const SIGSTKSZ* = cint(8192) +const SIG_DFL* = cast[Sighandler](0) +const SIG_ERR* = cast[Sighandler](-1) +const SIG_IGN* = cast[Sighandler](1) # <sys/ipc.h> const IPC_CREAT* = cint(512) diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index f2a71d1bd..003414a6a 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -414,6 +414,10 @@ var SS_ONSTACK* {.importc: "SS_ONSTACK", header: "<signal.h>".}: cint var SS_DISABLE* {.importc: "SS_DISABLE", header: "<signal.h>".}: cint var MINSIGSTKSZ* {.importc: "MINSIGSTKSZ", header: "<signal.h>".}: cint var SIGSTKSZ* {.importc: "SIGSTKSZ", header: "<signal.h>".}: cint +var SIG_HOLD* {.importc: "SIG_HOLD", header: "<signal.h>".}: Sighandler +var SIG_DFL* {.importc: "SIG_DFL", header: "<signal.h>".}: Sighandler +var SIG_ERR* {.importc: "SIG_ERR", header: "<signal.h>".}: Sighandler +var SIG_IGN* {.importc: "SIG_IGN", header: "<signal.h>".}: Sighandler # <sys/ipc.h> var IPC_CREAT* {.importc: "IPC_CREAT", header: "<sys/ipc.h>".}: cint diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 1697384e0..8c1cf6b18 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -163,8 +163,8 @@ include includes/asyncfutures type PDispatcherBase = ref object of RootRef - timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]] - callbacks: Deque[proc ()] + timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]] + callbacks*: Deque[proc ()] proc processTimers(p: PDispatcherBase) {.inline.} = #Process just part if timers at a step 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 ce4c9a9c9..89b216b25 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -301,13 +301,13 @@ 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.") - hint("Processing " & prc[0].getName & " as an async proc.") + let prcName = prc.name.getName - let returnType = prc[3][0] + let returnType = prc.params[0] var baseType: NimNode # Verify that the return type is a Future[T] if returnType.kind == nnkBracketExpr: @@ -326,9 +326,9 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = let subtypeIsVoid = returnType.kind == nnkEmpty or (baseType.kind == nnkIdent and returnType[1].ident == !"void") - let futureVarIdents = getFutureVarIdents(prc[3]) + let futureVarIdents = getFutureVarIdents(prc.params) - var outerProcBody = newNimNode(nnkStmtList, prc[6]) + var outerProcBody = newNimNode(nnkStmtList, prc.body) # -> var retFuture = newFuture[T]() var retFutureSym = genSym(nskVar, "retFuture") @@ -338,10 +338,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = outerProcBody.add( newVarStmt(retFutureSym, newCall( - newNimNode(nnkBracketExpr, prc[6]).add( + newNimNode(nnkBracketExpr, prc.body).add( newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`. subRetType), - newLit(prc[0].getName)))) # Get type from return type of this proc + newLit(prcName)))) # Get type from return type of this proc # -> iterator nameIter(): FutureBase {.closure.} = # -> {.push warning[resultshadowed]: off.} @@ -349,8 +349,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = # -> {.pop.} # -> <proc_body> # -> complete(retFuture, result) - var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter") - var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, + var iteratorNameSym = genSym(nskIterator, $prcName & "Iter") + var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid, futureVarIdents, nil) # don't do anything with forward bodies (empty) if procBody.kind != nnkEmpty: @@ -362,7 +362,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = newIdentNode("warning"), newIdentNode("resultshadowed")), newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.} - procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add( + procBody.insert(1, newNimNode(nnkVarSection, prc.body).add( newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T procBody.insert(2, newNimNode(nnkPragma).add( @@ -377,35 +377,32 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")], procBody, nnkIteratorDef) - closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure")) + closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body) + closureIterator.addPragma(newIdentNode("closure")) + closureIterator.addPragma(newIdentNode("gcsafe")) outerProcBody.add(closureIterator) # -> createCb(retFuture) #var cbName = newIdentNode("cb") var procCb = getAst createCb(retFutureSym, iteratorNameSym, - newStrLitNode(prc[0].getName), + newStrLitNode(prcName), createFutureVarCompletions(futureVarIdents, nil)) outerProcBody.add procCb # -> return retFuture - outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym) + outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym) result = prc - # Remove the 'async' pragma. - for i in 0 .. <result[4].len: - if result[4][i].kind == nnkIdent and result[4][i].ident == !"async": - result[4].del(i) - result[4] = newEmptyNode() if subtypeIsVoid: # Add discardable pragma. if returnType.kind == nnkEmpty: # Add Future[void] - result[3][0] = parseExpr("Future[void]") + result.params[0] = parseExpr("Future[void]") if procBody.kind != nnkEmpty: - result[6] = outerProcBody + result.body = outerProcBody #echo(treeRepr(result)) - #if prc[0].getName == "recvLineInto": + #if prcName == "recvLineInto": # echo(toStrLit(result)) macro async*(prc: untyped): untyped = @@ -529,8 +526,6 @@ macro multisync*(prc: untyped): untyped = ## ## The generated async procedures use the ``async`` macro, whereas the ## generated synchronous procedures simply strip off the ``await`` calls. - hint("Processing " & prc[0].getName & " as a multisync proc.") - let (sync, asyncPrc) = splitProc(prc) result = newStmtList() result.add(asyncSingleProc(asyncPrc)) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 9f73bc3cf..5de65efe0 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -533,15 +533,13 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], else: var c = "" while true: - let recvFut = recv(socket, 1, flags) - c = recvFut.read() + c = await recv(socket, 1, flags) if c.len == 0: resString.mget.setLen(0) resString.complete() return if c == "\r": - let recvFut = recv(socket, 1, flags) # Skip \L - c = recvFut.read() + c = await recv(socket, 1, flags) # Skip \L assert c == "\L" addNLIfEmpty() resString.complete() 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/distros.nim b/lib/pure/distros.nim index 896497630..0adba5b1e 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -158,7 +158,7 @@ proc detectOsImpl(d: Distribution): bool = of Distribution.ArchLinux: result = "arch" in toLowerAscii(uname()) of Distribution.OpenSUSE: - result = "suse" in toLowerAscii(uname()) + result = "suse" in toLowerAscii(uname()) or "suse" in toLowerAscii(release()) of Distribution.GoboLinux: result = "-Gobo " in uname() of Distribution.OpenMandriva: 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/includes/asynccommon.nim b/lib/pure/includes/asynccommon.nim index a7d2f803f..8b760c66a 100644 --- a/lib/pure/includes/asynccommon.nim +++ b/lib/pure/includes/asynccommon.nim @@ -3,7 +3,7 @@ template newAsyncNativeSocketImpl(domain, sockType, protocol) = if handle == osInvalidSocket: raiseOSError(osLastError()) handle.setBlocking(false) - when defined(macosx): + when defined(macosx) and not defined(nimdoc): handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) result = handle.AsyncFD register(result) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 564f952d3..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("\\\"") @@ -1608,7 +1608,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = result = processType(newIdentNode(typeName), obj, jsonNode, true) of "seq": let seqT = typeSym[1] - let forLoopI = newIdentNode("i") + let forLoopI = genSym(nskForVar, "i") let indexerNode = createJsonIndexer(jsonNode, forLoopI) let constructorNode = createConstructor(seqT, indexerNode) @@ -1616,12 +1616,25 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = result = quote do: ( var list: `typeSym` = @[]; - # if `jsonNode`.kind != JArray: - # # TODO: Improve error message. - # raise newException(ValueError, "Expected a list") + verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`)); for `forLoopI` in 0 .. <`jsonNode`.len: list.add(`constructorNode`); list ) + of "array": + let arrayT = typeSym[2] + let forLoopI = genSym(nskForVar, "i") + let indexerNode = createJsonIndexer(jsonNode, forLoopI) + let constructorNode = createConstructor(arrayT, indexerNode) + + # Create a statement expression containing a for loop. + result = quote do: + ( + var list: `typeSym`; + verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`)); + for `forLoopI` in 0 .. <`jsonNode`.len: list[`forLoopI`] =`constructorNode`; + list + ) + else: # Generic type. let obj = getType(typeSym) 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/os.nim b/lib/pure/os.nim index 98b6aa309..f7bcfb60e 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -666,29 +666,38 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} else: raiseOSError(osLastError(), $strerror(errno)) -proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [ReadIOEffect, WriteIOEffect].} = - ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. +proc tryMoveFSObject(source, dest: string): bool = + ## Moves a file or directory from `source` to `dest`. Returns false in case + ## of `EXDEV` error. In case of other errors `OSError` is raised. Returns + ## true in case of success. when defined(Windows): when useWinUnicode: let s = newWideCString(source) let d = newWideCString(dest) - if moveFileW(s, d) == 0'i32: raiseOSError(osLastError()) + if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError()) else: - if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError()) + if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError()) else: if c_rename(source, dest) != 0'i32: let err = osLastError() if err == EXDEV.OSErrorCode: - # Fallback to copy & del - copyFile(source, dest) - try: - removeFile(source) - except: - discard tryRemoveFile(dest) - raise + return false else: raiseOSError(err, $strerror(errno)) + return true + +proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", + tags: [ReadIOEffect, WriteIOEffect].} = + ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. + if not tryMoveFSObject(source, dest): + when not defined(windows): + # Fallback to copy & del + copyFile(source, dest) + try: + removeFile(source) + except: + discard tryRemoveFile(dest) + raise proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", tags: [ExecIOEffect].} = @@ -1369,6 +1378,14 @@ proc exclFilePermissions*(filename: string, ## setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions) +proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} = + ## Moves a directory from `source` to `dest`. If this fails, `OSError` is raised. + if not tryMoveFSObject(source, dest): + when not defined(windows): + # Fallback to copy & del + copyDir(source, dest) + removeDir(source) + include ospaths proc expandSymlink*(symlinkPath: string): string = @@ -1412,7 +1429,7 @@ when defined(nimdoc): proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = ## Returns the `i`-th `command line argument`:idx: given to the application. ## - ## `i` should be in the range `1..paramCount()`, the `EInvalidIndex` + ## `i` should be in the range `1..paramCount()`, the `IndexError` ## exception will be raised for invalid values. Instead of iterating over ## `paramCount() <#paramCount>`_ with this proc you can call the ## convenience `commandLineParams() <#commandLineParams>`_. @@ -1450,7 +1467,8 @@ elif defined(windows): tags: [ReadIOEffect].} = # Docstring in nimdoc block. if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine()) - return TaintedString(ownArgv[i]) + if i < ownArgv.len and i >= 0: return TaintedString(ownArgv[i]) + raise newException(IndexError, "invalid index") elif not defined(createNimRtl) and not(defined(posix) and appType == "lib") and diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index 7720fb2a6..fa5342fcf 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -516,7 +516,16 @@ when declared(getEnv) or defined(nimscript): proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the config directory of the current user for applications. + ## + ## On non-Windows OSs, this proc conforms to the XDG Base Directory + ## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment + ## variable if it is set, and returns the default configuration directory, + ## "~/.config/", otherwise. + ## + ## An OS-dependent trailing slash is always present at the end of the + ## returned string; `\\` on Windows and `/` on all other OSs. when defined(windows): return string(getEnv("APPDATA")) & "\\" + elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/" else: return string(getEnv("HOME")) & "/.config/" proc getTempDir*(): string {.rtl, extern: "nos$1", diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index c94a65a63..23c8546c4 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -863,18 +863,15 @@ elif not defined(useNimRtl): if data.workingDir.len > 0: setCurrentDir($data.workingDir) var pid: Pid - var err: OSErrorCode if data.optionPoUsePath: res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) - if res != 0'i32: err = osLastError() else: res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) - if res != 0'i32: err = osLastError() discard posix_spawn_file_actions_destroy(fops) discard posix_spawnattr_destroy(attr) - if res != 0'i32: raiseOSError(err) + if res != 0'i32: raiseOSError(OSErrorCode(res)) return pid else: diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 28681a11f..5bdd3bc40 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -540,8 +540,8 @@ proc writeConfig*(dict: Config, filename: string) = ## Writes the contents of the table to the specified configuration file. ## Note: Comment statement will be ignored. let file = open(filename, fmWrite) - let fileStream = newFileStream(filename) - defer: fileStream.close() + defer: file.close() + let fileStream = newFileStream(file) 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..1bda94d14 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 @@ -98,46 +98,47 @@ elif defined(windows): elif defined(JS): type - Time* = ref TimeObj - TimeObj {.importc.} = object - getDay: proc (): int {.tags: [], raises: [], benign.} - getFullYear: proc (): int {.tags: [], raises: [], benign.} - getHours: proc (): int {.tags: [], raises: [], benign.} - getMilliseconds: proc (): int {.tags: [], raises: [], benign.} - getMinutes: proc (): int {.tags: [], raises: [], benign.} - getMonth: proc (): int {.tags: [], raises: [], benign.} - getSeconds: proc (): int {.tags: [], raises: [], benign.} - getTime: proc (): int {.tags: [], raises: [], noSideEffect, benign.} - getTimezoneOffset: proc (): int {.tags: [], raises: [], benign.} - getDate: proc (): int {.tags: [], raises: [], benign.} - getUTCDate: proc (): int {.tags: [], raises: [], benign.} - getUTCFullYear: proc (): int {.tags: [], raises: [], benign.} - getUTCHours: proc (): int {.tags: [], raises: [], benign.} - getUTCMilliseconds: proc (): int {.tags: [], raises: [], benign.} - getUTCMinutes: proc (): int {.tags: [], raises: [], benign.} - getUTCMonth: proc (): int {.tags: [], raises: [], benign.} - getUTCSeconds: proc (): int {.tags: [], raises: [], benign.} - getUTCDay: proc (): int {.tags: [], raises: [], benign.} - getYear: proc (): int {.tags: [], raises: [], benign.} - parse: proc (s: cstring): Time {.tags: [], raises: [], benign.} - setDate: proc (x: int) {.tags: [], raises: [], benign.} - setFullYear: proc (x: int) {.tags: [], raises: [], benign.} - setHours: proc (x: int) {.tags: [], raises: [], benign.} - setMilliseconds: proc (x: int) {.tags: [], raises: [], benign.} - setMinutes: proc (x: int) {.tags: [], raises: [], benign.} - setMonth: proc (x: int) {.tags: [], raises: [], benign.} - setSeconds: proc (x: int) {.tags: [], raises: [], benign.} - setTime: proc (x: int) {.tags: [], raises: [], benign.} - setUTCDate: proc (x: int) {.tags: [], raises: [], benign.} - setUTCFullYear: proc (x: int) {.tags: [], raises: [], benign.} - setUTCHours: proc (x: int) {.tags: [], raises: [], benign.} - setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], benign.} - setUTCMinutes: proc (x: int) {.tags: [], raises: [], benign.} - setUTCMonth: proc (x: int) {.tags: [], raises: [], benign.} - setUTCSeconds: proc (x: int) {.tags: [], raises: [], benign.} - setYear: proc (x: int) {.tags: [], raises: [], benign.} - toGMTString: proc (): cstring {.tags: [], raises: [], benign.} - toLocaleString: proc (): cstring {.tags: [], raises: [], benign.} + TimeBase = float + Time* = distinct TimeBase + + proc getDay(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getHours(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getTime(t: Time): int {.tags: [], raises: [], noSideEffect, benign, importcpp.} + proc getTimezoneOffset(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getDate(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCDate(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCHours(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getUTCDay(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc getYear(t: Time): int {.tags: [], raises: [], benign, importcpp.} + proc parse(t: Time; s: cstring): Time {.tags: [], raises: [], benign, importcpp.} + proc setDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setTime(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setUTCDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setUTCFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setUTCHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setUTCMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setUTCMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setUTCMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setUTCSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc setYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.} + proc toGMTString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.} + proc toLocaleString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.} type TimeInfo* = object of RootObj ## represents a time in different parts @@ -225,17 +226,26 @@ proc `-`*(a, b: Time): int64 {. proc `<`*(a, b: Time): bool {. rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect.} = ## returns true iff ``a < b``, that is iff a happened before b. - result = a - b < 0 + when defined(js): + result = TimeBase(a) < TimeBase(b) + else: + result = a - b < 0 proc `<=` * (a, b: Time): bool {. rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect.}= ## returns true iff ``a <= b``. - result = a - b <= 0 + when defined(js): + result = TimeBase(a) <= TimeBase(b) + else: + result = a - b <= 0 proc `==`*(a, b: Time): bool {. rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect.} = ## returns true if ``a == b``, that is if both times represent the same value - result = a - b == 0 + when defined(js): + result = TimeBase(a) == TimeBase(b) + else: + result = a - b == 0 proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.} ## returns the offset of the local (non-DST) timezone in seconds west of UTC. @@ -479,7 +489,7 @@ proc `-=`*(t: var Time, ti: TimeInterval) = t = toTime(getLocalTime(t) - ti) proc `-`*(t: Time, ti: TimeInterval): Time = - ## adds the interval `ti` to Time `t` + ## subtracts the interval `ti` from Time `t` ## ## ``echo getTime() - 1.day`` result = toTime(getLocalTime(t) - ti) diff --git a/lib/system.nim b/lib/system.nim index 9b41253cc..8f653c1e0 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -49,6 +49,9 @@ type cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr`` ## operator to get a pointer to a variable + + typedesc* {.magic: TypeDesc.} ## meta type to denote a type description + const on* = true ## alias for ``true`` off* = false ## alias for ``false`` @@ -56,6 +59,15 @@ const {.push warning[GcMem]: off, warning[Uninit]: off.} {.push hints: off.} +proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} + ## Constructs an `or` meta class + +proc `and` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} + ## Constructs an `and` meta class + +proc `not` *(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} + ## Constructs an `not` meta class + type Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer, ## bool, character, and enumeration types @@ -66,11 +78,11 @@ type `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type `nil` {.magic: "Nil".} + expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates) - ## **Deprecated** since version 0.15. Use ``untyped`` instead. + ## **Deprecated** since version 0.15. Use ``untyped`` instead. stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates) ## **Deprecated** since version 0.15. Use ``typed`` instead. - typedesc* {.magic: TypeDesc.} ## meta type to denote a type description void* {.magic: "VoidType".} ## meta type to denote the absence of any type auto* {.magic: Expr.} ## meta type for automatic type determination any* = distinct auto ## meta type for any supported type @@ -430,6 +442,7 @@ type ## providing an exception message ## is bad style. trace: string + up: ref Exception # used for stacking exceptions. Not exported! SystemError* = object of Exception ## \ ## Abstract class for exceptions that the runtime system raises. @@ -1056,7 +1069,7 @@ proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.} ## ``floor(x/y)``. ## ## .. code-block:: Nim - ## (7 div 5) == 2 + ## (7 div 5) == 1 proc `mod`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ModU", noSideEffect.} ## computes the integer modulo operation (remainder). @@ -1332,6 +1345,7 @@ const hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") + nimEnableCovariance* = defined(nimEnableCovariance) # or true when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): # tcc doesn't support TLS @@ -1879,7 +1893,7 @@ const NimMajor*: int = 0 ## is the major number of Nim's version. - NimMinor*: int = 16 + NimMinor*: int = 17 ## is the minor number of Nim's version. NimPatch*: int = 1 @@ -2034,6 +2048,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 @@ -2428,18 +2450,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 @@ -2449,33 +2459,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. @@ -3210,16 +3274,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 @@ -3458,7 +3512,7 @@ proc `*=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {. proc `+=`*[T: float|float32|float64] (x: var T, y: T) {. inline, noSideEffect.} = - ## Increments in placee a floating point number + ## Increments in place a floating point number x = x + y proc `-=`*[T: float|float32|float64] (x: var T, y: T) {. @@ -3721,7 +3775,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/excpt.nim b/lib/system/excpt.nim index 55f283d2d..6c163f711 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -90,12 +90,11 @@ proc popSafePoint {.compilerRtl, inl.} = excHandler = excHandler.prev proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = - #if e.parent.isNil: - # e.parent = currException + e.up = currException currException = e proc popCurrentException {.compilerRtl, inl.} = - currException = nil # currException.parent + currException = currException.up # some platforms have native support for stack traces: const @@ -247,6 +246,18 @@ when false: pushCurrentException(e) c_longjmp(excHandler.context, 1) +var onUnhandledException*: (proc (errorMsg: string) {. + nimcall.}) ## set this error \ + ## handler to override the existing behaviour on an unhandled exception. + ## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``. + ## Unstable API. + +template unhandled(buf, body) = + if onUnhandledException != nil: + onUnhandledException($buf) + else: + body + proc raiseExceptionAux(e: ref Exception) = if localRaiseHook != nil: if not localRaiseHook(e): return @@ -277,7 +288,9 @@ proc raiseExceptionAux(e: ref Exception) = add(buf, " [") add(buf, $e.name) add(buf, "]\n") - showErrorMessage(buf) + unhandled(buf): + showErrorMessage(buf) + quitOrDebug() else: # ugly, but avoids heap allocations :-) template xadd(buf, s, slen: expr) = @@ -293,8 +306,9 @@ proc raiseExceptionAux(e: ref Exception) = add(buf, " [") xadd(buf, e.name, e.name.len) add(buf, "]\n") - showErrorMessage(buf) - quitOrDebug() + unhandled(buf): + showErrorMessage(buf) + quitOrDebug() proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = if e.name.isNil: e.name = ename 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/jssys.nim b/lib/system/jssys.nim index 8a81a550a..768f9bc17 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -446,7 +446,7 @@ when defined(kwin): print(buf); """ -elif defined(nodejs): +elif not defined(nimOldEcho): proc ewriteln(x: cstring) = log(x) proc rawEcho {.compilerproc, asmNoStackFrame.} = 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/lib/windows/winlean.nim b/lib/windows/winlean.nim index 164499543..7a221ceb1 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -291,6 +291,14 @@ const FILE_ATTRIBUTE_TEMPORARY* = 256'i32 MAX_PATH* = 260 + + MOVEFILE_COPY_ALLOWED* = 0x2'i32 + MOVEFILE_CREATE_HARDLINK* = 0x10'i32 + MOVEFILE_DELAY_UNTIL_REBOOT* = 0x4'i32 + MOVEFILE_FAIL_IF_NOT_TRACKABLE* = 0x20'i32 + MOVEFILE_REPLACE_EXISTING* = 0x1'i32 + MOVEFILE_WRITE_THROUGH* = 0x8'i32 + type WIN32_FIND_DATA* {.pure.} = object dwFileAttributes*: int32 @@ -342,6 +350,9 @@ when useWinUnicode: proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {. importc: "MoveFileW", stdcall, dynlib: "kernel32".} + proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString, + flags: DWORD): WINBOOL {. + importc: "MoveFileExW", stdcall, dynlib: "kernel32".} proc getEnvironmentStringsW*(): WideCString {. stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".} @@ -369,6 +380,9 @@ else: proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {. importc: "MoveFileA", stdcall, dynlib: "kernel32".} + proc moveFileExA*(lpExistingFileName, lpNewFileName: WideCString, + flags: DWORD): WINBOOL {. + importc: "MoveFileExA", stdcall, dynlib: "kernel32".} proc getEnvironmentStringsA*(): cstring {. stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".} diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 5521476d9..ff18fc2c2 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -26,7 +26,7 @@ when useWinVersion: from winlean import SocketHandle else: const - versions = "(|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)" + versions = "(|.38|.39|.41|.43|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)" when defined(macosx): const DLLSSLName = "libssl" & versions & ".dylib" 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/tasyncRecvLine.nim b/tests/async/tasyncRecvLine.nim new file mode 100644 index 000000000..679831b27 --- /dev/null +++ b/tests/async/tasyncRecvLine.nim @@ -0,0 +1,54 @@ +discard """ + file: "tasyncRecvLine.nim" + output: ''' +Hello World +Hello World +''' +""" + +import asyncdispatch, asyncnet + +const recvLinePort = Port(6047) + +proc setupTestServer(): AsyncSocket = + result = newAsyncSocket() + result.setSockOpt(OptReuseAddr, true) + result.bindAddr(recvLinePort) + result.listen() + +proc testUnbuffered(): Future[void] {.async.} = + let serverSock = setupTestServer() + let serverAcceptClientFut = serverSock.accept() + + let clientSock = newAsyncSocket(buffered = false) + let clientConnectFut = clientSock.connect("localhost", recvLinePort) + + let serverAcceptedClient = await serverAcceptClientFut + await clientConnectFut + + await serverAcceptedClient.send("Hello World\c\L") + + echo await clientSock.recvLine() + + clientSock.close() + serverSock.close() + +proc testBuffered(): Future[void] {.async.} = + let serverSock = setupTestServer() + let serverAcceptClientFut = serverSock.accept() + + let clientSock = newAsyncSocket(buffered = true) + let clientConnectFut = clientSock.connect("localhost", recvLinePort) + + let serverAcceptedClient = await serverAcceptClientFut + await clientConnectFut + + await serverAcceptedClient.send("Hello World\c\L") + + echo await clientSock.recvLine() + + clientSock.close() + serverSock.close() + +waitFor testUnbuffered() +waitFor testBuffered() diff --git a/tests/async/tasync_forward.nim b/tests/async/tasync_forward.nim index ffb7acafd..99527032f 100644 --- a/tests/async/tasync_forward.nim +++ b/tests/async/tasync_forward.nim @@ -7,3 +7,12 @@ proc foo {.async.} proc foo {.async.} = discard + +# With additional pragmas: +proc bar {.async, cdecl.} + +proc bar {.async.} = + discard + +proc verifyCdeclPresent(p: proc : Future[void] {.cdecl.}) = discard +verifyCdeclPresent(bar) diff --git a/tests/async/tasyncall.nim b/tests/async/tasyncall.nim index 63b2945a6..7daecd9ef 100644 --- a/tests/async/tasyncall.nim +++ b/tests/async/tasyncall.nim @@ -2,19 +2,19 @@ discard """ file: "tasyncall.nim" exitcode: 0 """ -import times, sequtils +import times, sequtils, unittest import asyncdispatch const taskCount = 10 - sleepDuration = 500 + sleepDuration = 50 proc futureWithValue(x: int): Future[int] {.async.} = await sleepAsync(sleepDuration) return x proc futureWithoutValue() {.async.} = - await sleepAsync(1000) + await sleepAsync(sleepDuration) proc testFuturesWithValue(x: int): seq[int] = var tasks = newSeq[Future[int]](taskCount) @@ -40,38 +40,39 @@ proc testVarargs(x, y, z: int): seq[int] = result = waitFor all(a, b, c) -block: - let - startTime = cpuTime() - results = testFuturesWithValue(42) - expected = repeat(42, taskCount) - execTime = cpuTime() - startTime - - doAssert execTime * 1000 < taskCount * sleepDuration - doAssert results == expected - -block: - let startTime = cpuTime() - testFuturesWithoutValues() - let execTime = cpuTime() - startTime - - doAssert execTime * 1000 < taskCount * sleepDuration - -block: - let - startTime = cpuTime() - results = testVarargs(1, 2, 3) - expected = @[1, 2, 3] - execTime = cpuTime() - startTime - - doAssert execTime * 100 < taskCount * sleepDuration - doAssert results == expected - -block: - let - noIntFuturesFut = all(newSeq[Future[int]]()) - noVoidFuturesFut = all(newSeq[Future[void]]()) - - doAssert noIntFuturesFut.finished and not noIntFuturesFut.failed - doAssert noVoidFuturesFut.finished and not noVoidFuturesFut.failed - doAssert noIntFuturesFut.read() == @[] +suite "tasyncall": + test "testFuturesWithValue": + let + startTime = cpuTime() + results = testFuturesWithValue(42) + expected = repeat(42, taskCount) + execTime = cpuTime() - startTime + + doAssert execTime * 1000 < taskCount * sleepDuration + doAssert results == expected + + test "testFuturesWithoutValues": + let startTime = cpuTime() + testFuturesWithoutValues() + let execTime = cpuTime() - startTime + + doAssert execTime * 1000 < taskCount * sleepDuration + + test "testVarargs": + let + startTime = cpuTime() + results = testVarargs(1, 2, 3) + expected = @[1, 2, 3] + execTime = cpuTime() - startTime + + doAssert execTime * 100 < taskCount * sleepDuration + doAssert results == expected + + test "all on seq[Future]": + let + noIntFuturesFut = all(newSeq[Future[int]]()) + noVoidFuturesFut = all(newSeq[Future[void]]()) + + doAssert noIntFuturesFut.finished and not noIntFuturesFut.failed + doAssert noVoidFuturesFut.finished and not noVoidFuturesFut.failed + doAssert noIntFuturesFut.read() == @[] diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index e2b9b94d5..034c2185c 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -508,10 +508,14 @@ else: freeAddrInfo(aiList) # for some reason Windows select doesn't return both # descriptors from first call, so we need to make 2 calls - var rcm1 = selector.select(1000) - var rcm2 = selector.select(1000) - let rcm = len(rcm1) + len(rcm2) - assert(rcm >= 2 and rcm <= 4) + var n = 0 + var rcm = selector.select(1000) + while n < 10 and len(rcm) < 2: + sleep(1000) + rcm = selector.select(1000) + inc(n) + + assert(len(rcm) == 2) var sockAddress = SockAddr() var addrLen = sizeof(sockAddress).Socklen 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/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim new file mode 100644 index 000000000..dfe4cb941 --- /dev/null +++ b/tests/cpp/tcovariancerules.nim @@ -0,0 +1,424 @@ +discard """ +cmd: "nim cpp $file" +output: ''' +cat +cat +dog +dog +cat +cat +dog +dog X +cat +cat +dog +dog +dog value +cat value +dog value +cat value +dog +dog +dog value +cat value +dog 1 +dog 2 +''' +""" + +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +import macros + +macro skipElse(n: untyped): typed = n[0] + +template acceptWithCovariance(x, otherwise): typed = + when nimEnableCovariance: + x + else: + reject(x) + skipElse(otherwise) + +type + Animal = object of RootObj + x: string + + Dog = object of Animal + y: int + + Cat = object of Animal + z: int + + AnimalRef = ref Animal + AnimalPtr = ptr Animal + + RefAlias[T] = ref T + +var dog = new(Dog) +dog.x = "dog" + +var cat = new(Cat) +cat.x = "cat" + +proc makeDerivedRef(x: string): ref Dog = + new(result) + result.x = x + +proc makeDerived(x: string): Dog = + result.x = x + +var covariantSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")] +var nonCovariantSeq = @[makeDerived("dog 1"), makeDerived("dog 2")] +var covariantArr = [makeDerivedRef("dog 1"), makeDerivedRef("dog 2")] +var nonCovariantArr = [makeDerived("dog 1"), makeDerived("dog 2")] + +proc wantsCovariantSeq1(s: seq[ref Animal]) = + for a in s: echo a.x + +proc wantsCovariantSeq2(s: seq[AnimalRef]) = + for a in s: echo a.x + +proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) = + for a in s: echo a.x + +proc wantsCovariantOperArray(s: openarray[ref Animal]) = + for a in s: echo a.x + +proc modifiesCovariantOperArray(s: var openarray[ref Animal]) = + for a in s: echo a.x + +proc modifiesDerivedOperArray(s: var openarray[ref Dog]) = + for a in s: echo a.x + +proc wantsNonCovariantOperArray(s: openarray[Animal]) = + for a in s: echo a.x + +proc wantsCovariantArray(s: array[2, ref Animal]) = + for a in s: echo a.x + +proc wantsNonCovariantSeq(s: seq[Animal]) = + for a in s: echo a.x + +proc wantsNonCovariantArray(s: array[2, Animal]) = + for a in s: echo a.x + +proc modifiesCovariantSeq(s: var seq[ref Animal]) = + for a in s: echo a.x + +proc modifiesCovariantArray(s: var array[2, ref Animal]) = + for a in s: echo a.x + +proc modifiesCovariantSeq(s: ptr seq[ref Animal]) = + for a in s[]: echo a.x + +proc modifiesCovariantArray(s: ptr array[2, ref Animal]) = + for a in s[]: echo a.x + +proc modifiesDerivedSeq(s: var seq[ref Dog]) = + for a in s: echo a.x + +proc modifiesDerivedArray(s: var array[2, ref Dog]) = + for a in s: echo a.x + +proc modifiesDerivedSeq(s: ptr seq[ref Dog]) = + for a in s[]: echo a.x + +proc modifiesDerivedArray(s: ptr array[2, ref Dog]) = + for a in s[]: echo a.x + +accept: + wantsCovariantArray([AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantArray([AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantArray([AnimalRef(cat), dog]) + + # there is a special rule that detects the base + # type of polymorphic arrays + wantsCovariantArray([cat, dog]) + +acceptWithCovariance: + wantsCovariantArray([cat, cat]) +else: + echo "cat" + echo "cat" + +var animalRefArray: array[2, ref Animal] + +accept: + animalRefArray = [AnimalRef(dog), AnimalRef(dog)] + animalRefArray = [AnimalRef(cat), dog] + +acceptWithCovariance: + animalRefArray = [dog, dog] + wantsCovariantArray animalRefArray +else: + echo "dog" + echo "dog" + +accept: + var animal: AnimalRef = dog + animal = cat + +var vdog: Dog +vdog.x = "dog value" +var vcat: Cat +vcat.x = "cat value" + +reject: + vcat = vdog + +# XXX: The next two cases seem incosistent, perhaps we should change the rules +accept: + # truncating copies are allowed + var vanimal: Animal = vdog + vanimal = vdog + +reject: + # truncating copies are not allowed with arrays + var vanimalArray: array[2, Animal] + var vdogArray = [vdog, vdog] + vanimalArray = vdogArray + +accept: + # a more explicit version of a truncating copy that + # should probably always remain allowed + var vnextnimal: Animal = Animal(vdog) + +proc wantsRefSeq(x: seq[AnimalRef]) = discard + +accept: + wantsCovariantSeq1(@[AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantSeq1(@[AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantSeq1(@[AnimalRef(cat), dog]) + wantsCovariantSeq1(@[cat, dog]) + + wantsCovariantSeq2(@[AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantSeq2(@[AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantSeq2(@[AnimalRef(cat), dog]) + wantsCovariantSeq2(@[cat, dog]) + + wantsCovariantSeq3(@[AnimalRef(dog), AnimalRef(dog)]) + wantsCovariantSeq3(@[AnimalRef(cat), AnimalRef(dog)]) + wantsCovariantSeq3(@[AnimalRef(cat), dog]) + wantsCovariantSeq3(@[cat, dog]) + + wantsCovariantOperArray([cat, dog]) + +acceptWithCovariance: + wantsCovariantSeq1(@[cat, cat]) + wantsCovariantSeq2(@[dog, makeDerivedRef("dog X")]) + # XXX: wantsCovariantSeq3(@[cat, cat]) + + wantsCovariantOperArray(@[cat, cat]) + wantsCovariantOperArray([dog, dog]) +else: + echo "cat" + echo "cat" + echo "dog" + echo "dog X" + echo "cat" + echo "cat" + echo "dog" + echo "dog" + +var dogRefs = @[dog, dog] +var dogRefsArray = [dog, dog] +var animalRefs = @[dog, cat] + +accept: + modifiesDerivedArray(dogRefsArray) + modifiesDerivedSeq(dogRefs) + +reject modifiesCovariantSeq(dogRefs) +reject modifiesCovariantSeq(addr(dogRefs)) +reject modifiesCovariantSeq(dogRefs.addr) + +reject modifiesCovariantArray([dog, dog]) +reject modifiesCovariantArray(dogRefsArray) +reject modifiesCovariantArray(addr(dogRefsArray)) +reject modifiesCovariantArray(dogRefsArray.addr) + +var dogValues = @[vdog, vdog] +var dogValuesArray = [vdog, vdog] +var animalValues = @[Animal(vdog), Animal(vcat)] +var animalValuesArray = [Animal(vdog), Animal(vcat)] + +wantsNonCovariantSeq animalValues +wantsNonCovariantArray animalValuesArray + +reject wantsNonCovariantSeq(dogRefs) +reject modifiesCovariantOperArray(dogRefs) +reject wantsNonCovariantArray(dogRefsArray) +reject wantsNonCovariantSeq(dogValues) +reject wantsNonCovariantArray(dogValuesArray) +reject modifiesValueArray() + +modifiesDerivedOperArray dogRefs +reject modifiesDerivedOperArray(dogValues) +reject modifiesDerivedOperArray(animalRefs) + +wantsNonCovariantOperArray animalValues +reject wantsNonCovariantOperArray(animalRefs) +reject wantsNonCovariantOperArray(dogRefs) +reject wantsNonCovariantOperArray(dogValues) + +var animalRefSeq: seq[ref Animal] + +accept: + animalRefSeq = @[AnimalRef(dog), AnimalRef(dog)] + animalRefSeq = @[AnimalRef(cat), dog] + +acceptWithCovariance: + animalRefSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")] + wantsCovariantSeq1(animalRefSeq) +else: + echo "dog 1" + echo "dog 2" + +var pdog: ptr Dog +var pcat: ptr Cat + +proc wantsPointer(x: ptr Animal) = + discard + +accept: + wantsPointer pdog + wantsPointer pcat + +# covariance should be disabled when var is involved +proc wantsVarPointer1(x: var ptr Animal) = + discard + +proc wantsVarPointer2(x: var AnimalPtr) = + discard + +reject wantsVarPointer1(pdog) +reject wantsVarPointer2(pcat) + +# covariance may be allowed for certain extern types + +{.emit: """ +template <class T> struct FN { typedef void (*type)(T); }; +template <class T> struct ARR { typedef T DataType[2]; DataType data; }; +""".} + +type + MyPtr {.importcpp: "'0 *"} [out T] = object + + MySeq {.importcpp: "ARR<'0>", nodecl} [out T] = object + data: array[2, T] + + MyAction {.importcpp: "FN<'0>::type"} [in T] = object + +var + cAnimal: MyPtr[Animal] + cDog: MyPtr[Dog] + cCat: MyPtr[Cat] + + cAnimalFn: MyAction[Animal] + cCatFn: MyAction[Cat] + cDogFn: MyAction[Dog] + + cRefAnimalFn: MyAction[ref Animal] + cRefCatFn: MyAction[ref Cat] + cRefDogFn: MyAction[ref Dog] + +accept: + cAnimal = cDog + cAnimal = cCat + + cDogFn = cAnimalFn + cCatFn = cAnimalFn + + cRefDogFn = cRefAnimalFn + cRefCatFn = cRefAnimalFn + +reject: cDogFn = cRefAnimalFn +reject: cCatFn = cRefAnimalFn + +reject: cCat = cDog +reject: cAnimalFn = cDogFn +reject: cAnimalFn = cCatFn +reject: cRefAnimalFn = cRefDogFn +reject: cRefAnimalFn = cRefCatFn +reject: cRefAnimalFn = cDogFn + +var + ptrPtrDog: ptr ptr Dog + ptrPtrAnimal: ptr ptr Animal + +reject: ptrPtrDog = ptrPtrAnimal + +# Try to break the rules by introducing some tricky +# double indirection types: +var + cPtrRefAnimal: MyPtr[ref Animal] + cPtrRefDog: MyPtr[ref Dog] + + cPtrAliasRefAnimal: MyPtr[RefAlias[Animal]] + cPtrAliasRefDog: MyPtr[RefAlias[Dog]] + + cDoublePtrAnimal: MyPtr[MyPtr[Animal]] + cDoublePtrDog: MyPtr[MyPtr[Dog]] + +reject: cPtrRefAnimal = cPtrRefDog +reject: cDoublePtrAnimal = cDoublePtrDog +reject: cRefAliasPtrAnimal = cRefAliasPtrDog +reject: cPtrRefAnimal = cRefAliasPtrDog +reject: cPtrAliasRefAnimal = cPtrRefDog + +var + # Array and Sequence types are covariant only + # when instantiated with ref or ptr types: + cAnimals: MySeq[ref Animal] + cDogs: MySeq[ref Dog] + + # "User-defined" pointer types should be OK too: + cAnimalPtrSeq: MySeq[MyPtr[Animal]] + cDogPtrSeq: MySeq[MyPtr[Dog]] + + # Value types shouldn't work: + cAnimalValues: MySeq[Animal] + cDogValues: MySeq[Dog] + + # Double pointer types should not work either: + cAnimalRefPtrSeq: MySeq[ref MyPtr[Animal]] + cDogRefPtrSeq: MySeq[ref MyPtr[Dog]] + cAnimalPtrPtrSeq: MySeq[ptr ptr Animal] + cDogPtrPtrSeq: MySeq[ptr ptr Dog] + +accept: + cAnimals = cDogs + cAnimalPtrSeq = cDogPtrSeq + +reject: cAnimalValues = cDogValues +reject: cAnimalRefPtrSeq = cDogRefPtrSeq +reject: cAnimalPtrPtrSeq = cDogPtrPtrSeq + +proc wantsAnimalSeq(x: MySeq[Animal]) = discard +proc wantsAnimalRefSeq(x: MySeq[ref Animal]) = discard +proc modifiesAnimalRefSeq(x: var MySeq[ref Animal]) = discard +proc usesAddressOfAnimalRefSeq(x: ptr MySeq[ref Animal]) = discard + +accept wantsAnimalSeq(cAnimalValues) +reject wantsAnimalSeq(cDogValues) +reject wantsAnimalSeq(cAnimals) + +reject wantsAnimalRefSeq(cAnimalValues) +reject wantsAnimalRefSeq(cDogValues) +accept wantsAnimalRefSeq(cAnimals) +accept wantsAnimalRefSeq(cDogs) + +reject modifiesAnimalRefSeq(cAnimalValues) +reject modifiesAnimalRefSeq(cDogValues) +accept modifiesAnimalRefSeq(cAnimals) +reject modifiesAnimalRefSeq(cDogs) + +reject usesAddressOfAnimalRefSeq(addr cAnimalValues) +reject usesAddressOfAnimalRefSeq(addr cDogValues) +accept usesAddressOfAnimalRefSeq(addr cAnimals) +reject usesAddressOfAnimalRefSeq(addr cDogs) + 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/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim new file mode 100644 index 000000000..ce7eb6022 --- /dev/null +++ b/tests/errmsgs/tinvalidinout.nim @@ -0,0 +1,26 @@ +discard """ +cmd: "nim check $file" +errormsg: "The `in` modifier can be used only with imported types" +nimout: ''' +tinvalidinout.nim(14, 7) Error: The `out` modifier can be used only with imported types +tinvalidinout.nim(17, 9) Error: The `in` modifier can be used only with imported types +tinvalidinout.nim(18, 9) Error: The `in` modifier can be used only with imported types +''' +""" + +type + Foo {.header: "foo.h", importcpp.} [in T] = object + + Bar[out X] = object + x: int + +proc f1[in T](x: T) = discard +proc f2[in T](x: T) {.importc: "f", header: "foo.h"} + +var + f: Foo[int] + b: Bar[string] + +f1 f +f2 b + diff --git a/tests/exception/tfinally3.nim b/tests/exception/tfinally3.nim index 8bccd1a7f..037ca9553 100644 --- a/tests/exception/tfinally3.nim +++ b/tests/exception/tfinally3.nim @@ -1,6 +1,11 @@ discard """ file: "tfinally3.nim" - output: "false" + output: '''false +Within finally->try +Traceback (most recent call last) +tfinally3.nim(24) tfinally3 +Error: unhandled exception: First [Exception]''' + exitCode: 1 """ # Test break in try statement: @@ -14,5 +19,11 @@ proc main: bool = echo main() #OUT false - - +# bug #5871 +try: + raise newException(Exception, "First") +finally: + try: + raise newException(Exception, "Within finally->try") + except: + echo getCurrentExceptionMsg() 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/float/tissue5821.nim b/tests/float/tissue5821.nim new file mode 100644 index 000000000..e8aa4a1d9 --- /dev/null +++ b/tests/float/tissue5821.nim @@ -0,0 +1,13 @@ +discard """ + file: "tissue5821.nim" + output: '''''' +""" +proc main(): void = + let a: float32 = 47.11'f32 + doAssert a == 47.11'f32 + + let b: float64 = 10.234402823e+38'f64 + doAssert b != 10.123402823e+38'f64 + doAssert b == 10.234402823e+38'f64 + +main() \ No newline at end of file diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim new file mode 100644 index 000000000..0920cb504 --- /dev/null +++ b/tests/generics/tfakecovariance.nim @@ -0,0 +1,78 @@ +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +type + BaseObj = object of RootObj + DerivedObj = object of BaseObj + NonDerivedObj = object + + Container[T] = object + +var base: BaseObj +var derived: DerivedObj +var nonDerived: NonDerivedObj + +var baseContainer: Container[BaseObj] +var derivedContainer: Container[DerivedObj] +var nonDerivedContainer: Container[NonDerivedObj] + +# We can fake covariance by listing some specific derived types that +# will be allowed with our overload. This is not a real covariance, +# because there will be multiple instantiations of the proc, but for +# many purposes, it will suffice: + +proc wantsSpecificContainers(c: Container[BaseObj or DerivedObj]) = discard + +accept wantsSpecificContainers(baseContainer) +accept wantsSpecificContainers(derivedContainer) + +reject wantsSpecificContainers(nonDerivedContainer) +reject wantsSpecificContainers(derived) + +# Now, let's make a more general solution able to catch all derived types: + +type + DerivedFrom[T] = concept type D + var derived: ref D + var base: ref T = derived + +proc wantsDerived(x: DerivedFrom[BaseObj]) = discard + +accept wantsDerived(base) +accept wantsDerived(derived) + +reject wantsDerived(nonDerived) +reject wantsDerived(baseContainer) + +proc wantsDerivedContainer(c: Container[DerivedFrom[BaseObj]]) = discard + +accept wantsDerivedContainer(baseContainer) +accept wantsDerivedContainer(derivedContainer) + +reject wantsDerivedContainer(nonDerivedContainer) + +# The previous solutions were solving the problem for a single overload. +# Let's solve it for multiple overloads by introducing a converter: + +type + OtherContainer[T] = object + +proc wantsBaseContainer1(c: OtherContainer[BaseObj]) = discard +proc wantsBaseContainer2(c: OtherContainer[BaseObj]) = discard + +converter derivedToBase(c: OtherContainer[DerivedFrom[BaseObj]]): OtherContainer[BaseObj] = discard + +block: + var baseContainer: OtherContainer[BaseObj] + var derivedContainer: OtherContainer[DerivedObj] + var nonDerivedContainer: OtherContainer[NonDerivedObj] + + accept wantsBaseContainer1(derivedContainer) + reject wantsBaseContainer1(nonDerivedContainer) + + accept wantsBaseContainer2(derivedContainer) + reject wantsBaseContainer2(nonDerivedContainer) + 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/tgenericconst.nim b/tests/generics/tgenericconst.nim new file mode 100644 index 000000000..3c86888df --- /dev/null +++ b/tests/generics/tgenericconst.nim @@ -0,0 +1,39 @@ +discard """ +output: ''' +@[1, 2] +@[3, 4] +1 +''' +""" + +# https://github.com/nim-lang/Nim/issues/5756 + +type + Vec*[N : static[int]] = object + x: int + arr*: array[N, int32] + + Mat*[M,N: static[int]] = object + x: int + arr*: array[M, Vec[N]] + +proc vec2*(x,y:int32) : Vec[2] = + result.arr = [x,y] + result.x = 10 + +proc mat2*(a,b: Vec[2]): Mat[2,2] = + result.arr = [a,b] + result.x = 20 + +const M = mat2(vec2(1, 2), vec2(3, 4)) + +let m1 = M +echo @(m1.arr[0].arr) +echo @(m1.arr[1].arr) + +proc foo = + let m2 = M + echo m1.arr[0].arr[0] + +foo() + 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/tptrinheritance.nim b/tests/generics/tptrinheritance.nim index 1e1115fa5..221b8777b 100644 --- a/tests/generics/tptrinheritance.nim +++ b/tests/generics/tptrinheritance.nim @@ -10,7 +10,7 @@ proc newMutableArrayAbstract*(): NSMutableArrayAbstract = discard template newMutableArray*(T: typedesc): NSMutableArray[T] = cast[NSMutableArray[T]](newMutableArrayAbstract()) -proc writeObjects*(p: NSPasteboard, o: ptr NSArray[NSPasteboardItem]) = discard +proc writeObjects*(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard let a = newMutableArray NSPasteboardItem var x: NSMutableArray[NSPasteboardItem] 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/tbyvar.nim b/tests/js/tbyvar.nim index f974049b9..705d62574 100644 --- a/tests/js/tbyvar.nim +++ b/tests/js/tbyvar.nim @@ -5,6 +5,12 @@ bar 12 foo 12 bar 12 2 +12.5 +(nums: @[5, 5, 10, 5, 5, 5, 5, 5, 5, 5]) +(nums: @[5, 5, 50, 5, 5, 5, 5, 5, 5, 5]) +(nums: @[5, 5, 45, 5, 5, 5, 5, 5, 5, 5]) +(nums: @[5, 5, 9, 5, 5, 5, 5, 5, 5, 5]) +asd ''' """ @@ -59,3 +65,58 @@ block: # Test var arg inside case expression. #5244 var a = "ok" foo(a) doAssert(a == "ok") + + +proc mainowar = + var x = 9.0 + x += 3.5 + echo x + +mainowar() + + +# bug #5608 + +type Foo = object + nums : seq[float] + +proc newFoo(len : int, default = 0.0) : Foo = + result = Foo() + result.nums = newSeq[float](len) + for i in 0..(len - 1): + result.nums[i] = default + +proc `[]=`(f : var Foo, i : int, v : float) = + f.nums[i] = v + +proc `[]`(f : Foo, i : int) : float = f.nums[i] + +proc `[]`(f : var Foo, i : int) : var float = f.nums[i] + +var f = newFoo(10,5) + +f[2] += 5 +echo f +f[2] *= 5 +echo f +f[2] -= 5 +echo f +f[2] /= 5 +echo f + +# regression for #5608 +import tables + +type + SomeObj = ref object + s: cstring + +var a = initTable[cstring, Table[cstring, SomeObj]]() + +var b = initTable[cstring, SomeObj]() + +b.add(cstring"b", SomeObj(s: cstring"asd")) + +a.add(cstring"a", b) + +echo a[cstring"a"][cstring"b"].s diff --git a/tests/js/tcopying.nim b/tests/js/tcopying.nim index 4f72d6ada..387df9cd3 100644 --- a/tests/js/tcopying.nim +++ b/tests/js/tcopying.nim @@ -1,5 +1,7 @@ discard """ output: '''123 +2 9 +2 9 ''' """ @@ -11,3 +13,25 @@ proc changeArray(a: var MyArray) = var a : MyArray changeArray(a) echo a[0] + +# bug #4703 +# Test 1 +block: + let ary1 = [1, 2, 3] + var ary2 = ary1 + + ary2[1] = 9 + + echo ary1[1], " ", ary2[1] + +# Test 2 +block: + type TestObj = ref object of RootObj + ary2: array[3, int] + + let ary1 = [1, 2, 3] + var obj = TestObj(ary2:ary1) + + obj.ary2[1] = 9 + + echo ary1[1], " ", obj.ary2[1] diff --git a/tests/js/testobjs.nim b/tests/js/testobjs.nim index 0166c0f38..dd66825ec 100644 --- a/tests/js/testobjs.nim +++ b/tests/js/testobjs.nim @@ -1,5 +1,7 @@ discard """ - action: run + output: '''{"columns":[{"t":null},{"t":null}]} +{"columns":[{"t":null},{"t":null}]} +''' """ ## Tests javascript object generation @@ -36,3 +38,19 @@ doAssert test.name == "Jorden" doAssert knight.age == 19 doAssert knight.item.price == 50 doAssert recurse1.next.next.data == 3 + +# bug #6035 +proc toJson*[T](data: T): cstring {.importc: "JSON.stringify".} + +type + Column = object + t: ref Column + + Test2 = object + columns: seq[Column] + +var test1 = Test2(columns: @[Column(t: nil), Column(t: nil)]) +let test2 = test1 + +echo toJSON(test1) +echo toJSON(test2) 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/trefbyvar.nim b/tests/js/trefbyvar.nim index 68dd36543..d440fcc64 100644 --- a/tests/js/trefbyvar.nim +++ b/tests/js/trefbyvar.nim @@ -2,7 +2,9 @@ discard """ output: '''0 5 0 -5''' +5 +@[1, 2] +~''' """ # bug #2476 @@ -33,3 +35,35 @@ proc main = echo t.m main() + +# bug #5974 +type + View* = object + data: ref seq[int] + +let a = View(data: new(seq[int])) +a.data[] = @[1, 2] + +echo a.data[] + +# bug #5379 +var input = newSeq[ref string]() +input.add(nil) +input.add(new string) +input[1][] = "~" +echo input[1][] + +# bug #5517 +type + TypeA1 = object of RootObj + a_impl: int + b_impl: string + c_impl: pointer + +proc initTypeA1(a: int; b: string; c: pointer = nil): TypeA1 = + result.a_impl = a + result.b_impl = b + result.c_impl = c + +let x = initTypeA1(1, "a") +doAssert($x == "(a_impl: 1, b_impl: a, c_impl: ...)") 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/js/ttimes.nim b/tests/js/ttimes.nim index 20ba14245..2868c6d0f 100644 --- a/tests/js/ttimes.nim +++ b/tests/js/ttimes.nim @@ -21,6 +21,9 @@ block timestampPersistenceTest: const timeString = "2017-03-21T12:34:56+03:00" timeStringGmt = "2017-03-21T09:34:56+00:00" + timeStringGmt2 = "2017-03-21T08:34:56+00:00" fmt = "yyyy-MM-dd'T'HH:mm:sszzz" + # XXX Check which one is the right solution here: - doAssert $timeString.parse(fmt).toTime().getGMTime() == timeStringGmt + let x = $timeString.parse(fmt).toTime().getGMTime() + doAssert x == timeStringGmt or x == timeStringGmt2 diff --git a/tests/macros/tclosuremacro.nim b/tests/macros/tclosuremacro.nim index c29fbe1c8..9f2137dec 100644 --- a/tests/macros/tclosuremacro.nim +++ b/tests/macros/tclosuremacro.nim @@ -6,10 +6,14 @@ discard """ 3 noReturn 6 +calling mystuff +yes +calling mystuff +yes ''' """ -import future +import future, macros proc twoParams(x: (int, int) -> int): int = result = x(5, 5) @@ -41,3 +45,30 @@ proc pass2(f: (int, int) -> int): (int) -> int = ((x: int) -> int) => f(2, x) echo pass2((x, y) => x + y)(4) + + + +proc register(name: string; x: proc()) = + echo "calling ", name + x() + +register("mystuff", proc () = + echo "yes" +) + +proc helper(x: NimNode): NimNode = + if x.kind == nnkProcDef: + result = copyNimTree(x) + result[0] = newEmptyNode() + result = newCall("register", newLit($x[0]), result) + else: + result = copyNimNode(x) + for i in 0..<x.len: + result.add helper(x[i]) + +macro m(x: untyped): untyped = + result = helper(x) + +m: + proc mystuff() = + echo "yes" 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/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 323b3e1ee..32d848e06 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -226,4 +226,24 @@ when isMainModule: let x = parseJson("""{ "field": 5}""") let data = to(x, FooBar) - doAssert data.field == 5.0 \ No newline at end of file + doAssert data.field == 5.0 + + block: + type + BirdColor = object + name: string + rgb: array[3, float] + + type + Bird = object + age: int + height: float + name: string + colors: array[2, BirdColor] + + var red = BirdColor(name: "red", rgb: [1.0, 0.0, 0.0]) + var blue = Birdcolor(name: "blue", rgb: [0.0, 0.0, 1.0]) + var b = Bird(age: 3, height: 1.734, name: "bardo", colors: [red, blue]) + let jnode = %b + let data = jnode.to(Bird) + doAssert data == b \ No newline at end of file diff --git a/tests/stdlib/tjsontestsuite.nim b/tests/stdlib/tjsontestsuite.nim new file mode 100644 index 000000000..06f783a73 --- /dev/null +++ b/tests/stdlib/tjsontestsuite.nim @@ -0,0 +1,384 @@ +## JSON tests based on https://github.com/nst/JSONTestSuite + +import unittest, + json, + strutils + +let parsing_testdata = { + "i_number_neg_int_huge_exp": """[-1e+9999]""", + "i_number_pos_double_huge_exp": """[1.5e+9999]""", + "i_object_key_lone_2nd_surrogate": """{"\uDFAA":0}""", + "i_string_1st_surrogate_but_2nd_missing": """["\uDADA"]""", + "i_string_1st_valid_surrogate_2nd_invalid": """["\uD888\u1234"]""", + "i_string_incomplete_surrogate_and_escape_valid": """["\uD800\n"]""", + "i_string_incomplete_surrogate_pair": """["\uDd1ea"]""", + "i_string_incomplete_surrogates_escape_valid": """["\uD800\uD800\n"]""", + "i_string_inverted_surrogates_U+1D11E": """["\uDd1e\uD834"]""", + "i_string_lone_second_surrogate": """["\uDFAA"]""", + "i_string_not_in_unicode_range": """[""]""", + "i_string_truncated-utf-8": """[""]""", + "i_string_unicode_U+10FFFE_nonchar": """["\uDBFF\uDFFE"]""", + "i_string_unicode_U+1FFFE_nonchar": """["\uD83F\uDFFE"]""", + "i_string_unicode_U+FDD0_nonchar": """["\uFDD0"]""", + "i_string_unicode_U+FFFE_nonchar": """["\uFFFE"]""", + "i_string_UTF-16_invalid_lonely_surrogate": """["\ud800"]""", + "i_string_UTF-16_invalid_surrogate": """["\ud800abc"]""", + "i_string_UTF-8_invalid_sequence": """["日ш"]""", + "i_structure_500_nested_arrays": """[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]""", + "i_structure_UTF-8_BOM_empty_object": """{}""", + "n_array_1_true_without_comma": """[1 true]""", + "n_array_a_invalid_utf8": """[a]""", + "n_array_colon_instead_of_comma": """["": 1]""", + "n_array_comma_after_close": """[""],""", + "n_array_comma_and_number": """[,1]""", + "n_array_double_comma": """[1,,2]""", + "n_array_double_extra_comma": """["x",,]""", + "n_array_extra_close": """["x"]]""", + "n_array_extra_comma": """["",]""", + "n_array_incomplete_invalid_value": """[x""", + "n_array_incomplete": """["x"""", + "n_array_inner_array_no_comma": """[3[4]]""", + "n_array_invalid_utf8": """[]""", + "n_array_items_separated_by_semicolon": """[1:2]""", + "n_array_just_comma": """[,]""", + "n_array_just_minus": """[-]""", + "n_array_missing_value": """[ , ""]""", + "n_array_newlines_unclosed": """["a",""", + "n_array_newlines_unclosed": """4""", + "n_array_newlines_unclosed": """,1,""", + "n_array_number_and_comma": """[1,]""", + "n_array_number_and_several_commas": """[1,,]""", + "n_array_spaces_vertical_tab_formfeed": """["a"\f]""", + "n_array_star_inside": """[*]""", + "n_array_unclosed": """[""""", + "n_array_unclosed_trailing_comma": """[1,""", + "n_array_unclosed_with_new_lines": """[1,""", + "n_array_unclosed_with_new_lines": """1""", + "n_array_unclosed_with_new_lines": """,1""", + "n_array_unclosed_with_object_inside": """[{}""", + "n_incomplete_false": """[fals]""", + "n_incomplete_null": """[nul]""", + "n_incomplete_true": """[tru]""", + "n_number_0.1.2": """[0.1.2]""", + "n_number_-01": """[-01]""", + "n_number_0.3e": """[0.3e]""", + "n_number_0.3e+": """[0.3e+]""", + "n_number_0_capital_E": """[0E]""", + "n_number_0_capital_E+": """[0E+]""", + "n_number_0.e1": """[0.e1]""", + "n_number_0e": """[0e]""", + "n_number_0e+": """[0e+]""", + "n_number_1_000": """[1 000.0]""", + "n_number_1.0e-": """[1.0e-]""", + "n_number_1.0e": """[1.0e]""", + "n_number_1.0e+": """[1.0e+]""", + "n_number_-1.0.": """[-1.0.]""", + "n_number_1eE2": """[1eE2]""", + "n_number_.-1": """[.-1]""", + "n_number_+1": """[+1]""", + "n_number_.2e-3": """[.2e-3]""", + "n_number_2.e-3": """[2.e-3]""", + "n_number_2.e+3": """[2.e+3]""", + "n_number_2.e3": """[2.e3]""", + "n_number_-2.": """[-2.]""", + "n_number_9.e+": """[9.e+]""", + "n_number_expression": """[1+2]""", + "n_number_hex_1_digit": """[0x1]""", + "n_number_hex_2_digits": """[0x42]""", + "n_number_infinity": """[Infinity]""", + "n_number_+Inf": """[+Inf]""", + "n_number_Inf": """[Inf]""", + "n_number_invalid+-": """[0e+-1]""", + "n_number_invalid-negative-real": """[-123.123foo]""", + "n_number_invalid-utf-8-in-bigger-int": """[123]""", + "n_number_invalid-utf-8-in-exponent": """[1e1]""", + "n_number_invalid-utf-8-in-int": """[0]""", + "n_number_++": """[++1234]""", + "n_number_minus_infinity": """[-Infinity]""", + "n_number_minus_sign_with_trailing_garbage": """[-foo]""", + "n_number_minus_space_1": """[- 1]""", + "n_number_-NaN": """[-NaN]""", + "n_number_NaN": """[NaN]""", + "n_number_neg_int_starting_with_zero": """[-012]""", + "n_number_neg_real_without_int_part": """[-.123]""", + "n_number_neg_with_garbage_at_end": """[-1x]""", + "n_number_real_garbage_after_e": """[1ea]""", + "n_number_real_with_invalid_utf8_after_e": """[1e]""", + "n_number_real_without_fractional_part": """[1.]""", + "n_number_starting_with_dot": """[.123]""", + "n_number_then_00": """1\x00""", + "n_number_U+FF11_fullwidth_digit_one": """[1]""", + "n_number_with_alpha_char": """[1.8011670033376514H-308]""", + "n_number_with_alpha": """[1.2a-3]""", + "n_number_with_leading_zero": """[012]""", + "n_object_bad_value": """["x", truth]""", + "n_object_bracket_key": """{[: "x"}""", + "n_object_comma_instead_of_colon": """{"x", null}""", + "n_object_double_colon": """{"x"::"b"}""", + "n_object_emoji": """{🇨🇭}""", + "n_object_garbage_at_end": """{"a":"a" 123}""", + "n_object_key_with_single_quotes": """{key: 'value'}""", + "n_object_missing_colon": """{"a" b}""", + "n_object_missing_key": """{:"b"}""", + "n_object_missing_semicolon": """{"a" "b"}""", + "n_object_missing_value": """{"a":""", + "n_object_no-colon": """{"a"""", + "n_object_non_string_key_but_huge_number_instead": """{9999E9999:1}""", + "n_object_non_string_key": """{1:1}""", + "n_object_pi_in_key_and_trailing_comma": """{"":"0",}""", + "n_object_repeated_null_null": """{null:null,null:null}""", + "n_object_several_trailing_commas": """{"id":0,,,,,}""", + "n_object_single_quote": """{'a':0}""", + "n_object_trailing_comma": """{"id":0,}""", + "n_object_trailing_comment": """{"a":"b"}/**/""", + "n_object_trailing_comment_open": """{"a":"b"}/**//""", + "n_object_trailing_comment_slash_open_incomplete": """{"a":"b"}/""", + "n_object_trailing_comment_slash_open": """{"a":"b"}//""", + "n_object_two_commas_in_a_row": """{"a":"b",,"c":"d"}""", + "n_object_unquoted_key": """{a: "b"}""", + "n_object_unterminated-value": """{"a":"a""", + "n_object_with_single_string": """{ "foo" : "bar", "a" }""", + "n_object_with_trailing_garbage": """{"a":"b"}#""", + "n_single_space": """ """, + "n_string_1_surrogate_then_escape": """["\uD800\"]""", + "n_string_1_surrogate_then_escape u1": """["\uD800\u1"]""", + "n_string_1_surrogate_then_escape u1x": """["\uD800\u1x"]""", + "n_string_1_surrogate_then_escape u": """["\uD800\u"]""", + "n_string_accentuated_char_no_quotes": """[é]""", + "n_string_backslash_00": """["\\x00"]""", + "n_string_escaped_backslash_bad": """["\\\"]""", + "n_string_escaped_ctrl_char_tab": """["\ "]""", + "n_string_escaped_emoji": """["\🌀"]""", + "n_string_escape_x": """["\x00"]""", + "n_string_incomplete_escaped_character": """["\u00A"]""", + "n_string_incomplete_escape": """["\"]""", + "n_string_incomplete_surrogate_escape_invalid": """["\uD800\uD800\x"]""", + "n_string_invalid_backslash_esc": """["\a"]""", + "n_string_invalid_unicode_escape": """["\uqqqq"]""", + "n_string_invalid_utf8_after_escape": """["\"]""", + "n_string_invalid-utf-8-in-escape": """["\u"]""", + "n_string_invalid_utf-8": """[""]""", + "n_string_iso_latin_1": """[""]""", + "n_string_leading_uescaped_thinspace": """[\u0020"asd"]""", + "n_string_lone_utf8_continuation_byte": """[""]""", + "n_string_no_quotes_with_bad_escape": """[\n]""", + "n_string_overlong_sequence_2_bytes": """[""]""", + "n_string_overlong_sequence_6_bytes": """[""]""", + "n_string_overlong_sequence_6_bytes_null": """[""]""", + "n_string_single_doublequote": """"""", + "n_string_single_quote": """['single quote']""", + "n_string_single_string_no_double_quotes": """abc""", + "n_string_start_escape_unclosed": """["\""", + "n_string_unescaped_ctrl_char": """["a\x00a"]""", + "n_string_unescaped_newline": """["new +line"]""", + "n_string_unescaped_tab": """[" "]""", + "n_string_unicode_CapitalU": """"\UA66D"""", + "n_string_UTF-16_incomplete_surrogate": """["\uD834\uDd"]""", + "n_string_UTF8_surrogate_U+D800": """[""]""", + "n_string_with_trailing_garbage": """""x""", + "n_structure_array_trailing_garbage": """[1]x""", + "n_structure_array_with_extra_array_close": """[1]]""", + "n_structure_array_with_unclosed_string": """["asd]""", + "n_structure_ascii-unicode-identifier": """aå""", + "n_structure_capitalized_True": """[True]""", + "n_structure_close_unopened_array": """1]""", + "n_structure_comma_instead_of_closing_brace": """{"x": true,""", + "n_structure_double_array": """[][]""", + "n_structure_end_array": """]""", + "n_structure_incomplete_UTF8_BOM": """{}""", + "n_structure_<.>": """<.>""", + "n_structure_lone-invalid-utf-8": """""", + "n_structure_lone-open-bracket": """[""", + "n_structure_null-byte-outside-string": """[\00]""", + "n_structure_<null>": """[<null>]""", + "n_structure_number_with_trailing_garbage": """2@""", + "n_structure_object_followed_by_closing_object": """{}}""", + "n_structure_object_unclosed_no_value": """{"":""", + "n_structure_object_with_comment": """{"a":/*comment*/"b"}""", + "n_structure_object_with_trailing_garbage": """{"a": true} "x"""", + "n_structure_open_array_apostrophe": """['""", + "n_structure_open_array_comma": """[,""", + "n_structure_open_array_open_object": """[{""", + "n_structure_open_array_open_string": """["a""", + "n_structure_open_array_string": """["a"""", + "n_structure_open_object_close_array": """{]""", + "n_structure_open_object_comma": """{,""", + "n_structure_open_object": """{""", + "n_structure_open_object_open_array": """{[""", + "n_structure_open_object_open_string": """{"a""", + "n_structure_open_object_string_with_apostrophes": """{'a'""", + "n_structure_open_open": """["\{["\{["\{["\{""", + "n_structure_single_point": """""", + "n_structure_single_star": """*""", + "n_structure_trailing_#": """{"a":"b"}#{}""", + "n_structure_U+2060_word_joined": """[]""", + "n_structure_uescaped_LF_before_string": """[\u000A""]""", + "n_structure_unclosed_array": """[1""", + "n_structure_unclosed_array_partial_null": """[ false, nul""", + "n_structure_unclosed_array_unfinished_false": """[ true, fals""", + "n_structure_unclosed_array_unfinished_true": """[ false, tru""", + "n_structure_unclosed_object": """{"asd":"asd"""", + "n_structure_unicode-identifier": """å""", + "n_structure_UTF8_BOM_no_data": """""", + "n_structure_whitespace_formfeed": """[]""", + "n_structure_whitespace_U+2060_word_joiner": """[]""", + "y_array_arraysWithSpaces": """[[] ]""", + "y_array_empty": """[]""", + "y_array_empty-string": """[""]""", + "y_array_ending_with_newline": """["a"]""", + "y_array_false": """[false]""", + "y_array_heterogeneous": """[null, 1, "1", {}]""", + "y_array_null": """[null]""", + "y_array_with_1_and_newline": """[1 +]""", + "y_array_with_leading_space": """ [1]""", + "y_array_with_several_null": """[1,null,null,null,2]""", + "y_array_with_trailing_space": """[2] """, + "y_number_0e+1": """[0e+1]""", + "y_number_0e1": """[0e1]""", + "y_number_after_space": """[ 4]""", + "y_number_double_close_to_zero": """[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]""", + "y_number_double_huge_neg_exp": """[123.456e-789]""", + "y_number_huge_exp": """[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]""", + "y_number_int_with_exp": """[20e1]""", + "y_number": """[123e65]""", + "y_number_minus_zero": """[-0]""", + "y_number_negative_int": """[-123]""", + "y_number_negative_one": """[-1]""", + "y_number_negative_zero": """[-0]""", + "y_number_real_capital_e": """[1E22]""", + "y_number_real_capital_e_neg_exp": """[1E-2]""", + "y_number_real_capital_e_pos_exp": """[1E+2]""", + "y_number_real_exponent": """[123e45]""", + "y_number_real_fraction_exponent": """[123.456e78]""", + "y_number_real_neg_exp": """[1e-2]""", + "y_number_real_neg_overflow": """[-123123e100000]""", + "y_number_real_pos_exponent": """[1e+2]""", + "y_number_real_pos_overflow": """[123123e100000]""", + "y_number_real_underflow": """[123e-10000000]""", + "y_number_simple_int": """[123]""", + "y_number_simple_real": """[123.456789]""", + "y_number_too_big_neg_int": """[-123123123123123123123123123123]""", + "y_number_too_big_pos_int": """[100000000000000000000]""", + "y_number_very_big_negative_int": """[-237462374673276894279832749832423479823246327846]""", + "y_object_basic": """{"asd":"sdf"}""", + "y_object_duplicated_key_and_value": """{"a":"b","a":"b"}""", + "y_object_duplicated_key": """{"a":"b","a":"c"}""", + "y_object_empty": """{}""", + "y_object_empty_key": """{"":0}""", + "y_object_escaped_null_in_key": """{"foo\u0000bar": 42}""", + "y_object_extreme_numbers": """{ "min": -1.0e+28, "max": 1.0e+28 }""", + "y_object": """{"asd":"sdf", "dfg":"fgh"}""", + "y_object_long_strings": """{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}""", + "y_object_simple": """{"a":[]}""", + "y_object_string_unicode": """{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }""", + "y_object_with_newlines": """{ +"a": "b" +}""", + "y_string_1_2_3_bytes_UTF-8_sequences": """["\u0060\u012a\u12AB"]""", + "y_string_accepted_surrogate_pair": """["\uD801\udc37"]""", + "y_string_accepted_surrogate_pairs": """["\ud83d\ude39\ud83d\udc8d"]""", + "y_string_allowed_escapes": """["\"\\\/\b\f\n\r\t"]""", + "y_string_backslash_and_u_escaped_zero": """["\\u0000"]""", + "y_string_backslash_doublequotes": """["\""]""", + "y_string_comments": """["a/*b*/c/*d//e"]""", + "y_string_double_escape_a": """["\\a"]""", + "y_string_double_escape_n": """["\\n"]""", + "y_string_escaped_control_character": """["\u0012"]""", + "y_string_escaped_noncharacter": """["\uFFFF"]""", + "y_string_in_array": """["asd"]""", + "y_string_in_array_with_leading_space": """[ "asd"]""", + "y_string_last_surrogates_1_and_2": """["\uDBFF\uDFFF"]""", + "y_string_newline_uescaped": """["new\u00A0line"]""", + "y_string_nonCharacterInUTF-8_U+10FFFF": """[""]""", + "y_string_nonCharacterInUTF-8_U+1FFFF": """[""]""", + "y_string_nonCharacterInUTF-8_U+FFFF": """[""]""", + "y_string_null_escape": """["\u0000"]""", + "y_string_one-byte-utf-8": """["\u002c"]""", + "y_string_pi": """["π"]""", + "y_string_simple_ascii": """["asd "]""", + "y_string_space": """" """", + "y_string_three-byte-utf-8": """["\u0821"]""", + "y_string_two-byte-utf-8": """["\u0123"]""", + "y_string_u+2028_line_sep": """[" "]""", + "y_string_u+2029_par_sep": """[" "]""", + "y_string_uEscape": """["\u0061\u30af\u30EA\u30b9"]""", + "y_string_unescaped_char_delete": """[""]""", + "y_string_unicode_2": """["⍂㈴⍂"]""", + "y_string_unicodeEscapedBackslash": """["\u005C"]""", + "y_string_unicode_escaped_double_quote": """["\u0022"]""", + "y_string_unicode": """["\uA66D"]""", + "y_string_unicode_U+200B_ZERO_WIDTH_SPACE": """["\u200B"]""", + "y_string_unicode_U+2064_invisible_plus": """["\u2064"]""", + "y_string_UTF-16_Surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF": """["\uD834\uDd1e"]""", + "y_string_utf8": """["€𝄞"]""", + "y_string_with_del_character": """["aa"]""", + "y_structure_lonely_false": """false""", + "y_structure_lonely_int": """42""", + "y_structure_lonely_negative_real": """-0.1""", + "y_structure_lonely_null": """null""", + "y_structure_lonely_string": """"asd"""", + "y_structure_lonely_true": """true""", + "y_structure_string_empty": """""""", + "y_structure_trailing_newline": """["a"]""", + "y_structure_true_in_array": """[true]""", + "y_structure_whitespace_array": """ [] """, +} + + +suite "JSON": + + test "Multiple parsing tests": + var test_is_failed = false + for test_item in parsing_testdata: + + let name = test_item[0] + let data = test_item[1] + var + parsed_successfully = false + parsed: JsonNode + exception_while_parsing = "" + exception_while_rendering = "" + + try: + parsed = parseJson(data) + parsed_successfully = true + except: + exception_while_parsing = getCurrentExceptionMsg() + + + proc echo_summary(msg: string) = + var rendered = "" + var render_successfully = false + if parsed_successfully: + try: + rendered = $parsed + render_successfully = true + except: + rendered = "[Fail to render:<$#>]" % getCurrentExceptionMsg() + else: + rendered = "<$#>" % exception_while_parsing + + echo name, repeat(' ', 60 - name.len), "[$#]" % msg, " ", rendered + + case name[0] + of 'y': + # Tests starting with y_ must parse + if not parsed_successfully: + echo_summary "Failed to parse" + test_is_failed = true + of 'n': + # Tests starting with n_ should not parse + if parsed_successfully: + echo_summary "Failed to raise exception" + of 'i': + if parsed_successfully: + echo_summary "OK" + else: + echo_summary "Not parsed" + + else: discard + + # FIXME: temporarily disabled until "y_" tests will succeed + # if test_is_failed: fail() diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index b05cb5a10..6a53a2964 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,7 +1,10 @@ discard """ output: '''{"age": 12, "bio": "\u042F Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} true -true''' +true +alpha 100 +omega 200 +''' """ import marshal @@ -83,3 +86,22 @@ var instance1 = Person(name: "Cletus", age: 12, echo($$instance1) echo(to[Person]($$instance1).bio == instance1.bio) echo(to[Person]($$instance1).blob == instance1.blob) + +# bug 5757 + +type + Something = object + x: string + y: int + +var data1 = """{"x": "alpha", "y": 100}""" +var data2 = """{"x": "omega", "y": 200}""" + +var r = to[Something](data1) + +echo r.x, " ", r.y + +r = to[Something](data2) + +echo r.x, " ", r.y + 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/template/tgenerictemplates.nim b/tests/template/tgenerictemplates.nim new file mode 100644 index 000000000..2c83bc0ec --- /dev/null +++ b/tests/template/tgenerictemplates.nim @@ -0,0 +1,13 @@ +type + SomeObj = object of RootObj + + Foo[T, U] = object + x: T + y: U + +template someTemplate[T](): tuple[id: int32, obj: T] = + var result: tuple[id: int32, obj: T] = (0'i32, T()) + result + +let ret = someTemplate[SomeObj]() + 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 100755..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/detect/detect.nim b/tools/detect/detect.nim index 3afe8ee67..1b016cef9 100644 --- a/tools/detect/detect.nim +++ b/tools/detect/detect.nim @@ -119,10 +119,14 @@ proc v(name: string, typ = "cint", no_other = false) = addf(tl, "#ifdef $3\n fprintf(f, \"const $1* = $2(%ld)\\n\", $3);\n#endif\n", n, t, name) - else: + of "cint", "cshort", "InAddrScalar", "TSa_Family": addf(tl, "#ifdef $3\n fprintf(f, \"const $1* = $2(%d)\\n\", $3);\n#endif\n", n, t, name) + else: + addf(tl, + "#ifdef $3\n fprintf(f, \"const $1* = cast[$2](%d)\\n\", $3);\n#endif\n", + n, t, name) header("<aio.h>") @@ -544,6 +548,11 @@ v("SS_DISABLE") v("MINSIGSTKSZ") v("SIGSTKSZ") +v("SIG_HOLD", "Sighandler") +v("SIG_DFL", "Sighandler") +v("SIG_ERR", "Sighandler") +v("SIG_IGN", "Sighandler") + header("<sys/ipc.h>") v("IPC_CREAT") v("IPC_EXCL") diff --git a/tools/finish.nim b/tools/finish.nim index cdbbdabe5..a6f689eac 100644 --- a/tools/finish.nim +++ b/tools/finish.nim @@ -92,7 +92,13 @@ when defined(windows): echo "Could not access 'config/nim.cfg' [Error]" proc addToPathEnv*(e: string) = - let p = getUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER) + var p: string + try: + p = getUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER) + except OSError: + p = getUnicodeValue( + r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", + "Path", HKEY_LOCAL_MACHINE) let x = if e.contains(Whitespace): "\"" & e & "\"" else: e setUnicodeValue(r"Environment", "Path", p & ";" & x, HKEY_CURRENT_USER) diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl index ce2db1c48..c4e0be55e 100644 --- a/tools/niminst/makefile.tmpl +++ b/tools/niminst/makefile.tmpl @@ -12,146 +12,143 @@ binDir = ?{firstBinPath(c).toUnix} koch := $(shell sh -c 'test -s ../koch.nim && echo "yes"') ifeq ($(koch),yes) - binDir = ../bin + binDir = ../bin endif ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"') uos := $(shell sh -c 'uname | tr "[:upper:]" "[:lower:]"') ifeq ($(uos),linux) - myos = linux - LINK_FLAGS += -ldl -lm + myos = linux + LINK_FLAGS += -ldl -lm endif ifeq ($(uos),dragonfly) - myos = freebsd - LINK_FLAGS += -lm + myos = freebsd + LINK_FLAGS += -lm endif ifeq ($(uos),freebsd) - myos= freebsd - CC = clang - LINKER = clang - LINK_FLAGS += -lm + myos= freebsd + CC = clang + LINKER = clang + LINK_FLAGS += -lm endif ifeq ($(uos),openbsd) - myos = openbsd - LINK_FLAGS += -lm + myos = openbsd + LINK_FLAGS += -lm endif ifeq ($(uos),netbsd) - myos = netbsd - LINK_FLAGS += -lm + myos = netbsd + LINK_FLAGS += -lm endif ifeq ($(uos),darwin) - myos = macosx - CC = clang - LINKER = clang - LINK_FLAGS += -ldl -lm - ifeq ($HOSTTYPE,x86_64) - ucpu = amd64 - endif + myos = macosx + CC = clang + LINKER = clang + LINK_FLAGS += -ldl -lm + ifeq ($(HOSTTYPE),x86_64) + ucpu = amd64 + endif endif ifeq ($(uos),aix) - myos = aix - LINK_FLAGS += -dl -lm + myos = aix + LINK_FLAGS += -dl -lm endif ifeq ($(uos),solaris) - myos = solaris - LINK_FLAGS += -ldl -lm -lsocket -lnsl + myos = solaris + LINK_FLAGS += -ldl -lm -lsocket -lnsl endif ifeq ($(uos),sun) - myos = solaris - LINK_FLAGS += -ldl -lm -lsocket -lnsl + myos = solaris + LINK_FLAGS += -ldl -lm -lsocket -lnsl endif ifeq ($(uos),haiku) - myos = haiku + myos = haiku endif ifndef uos - @echo "Error: unknown operating system: $(uos)" - @exit 1 + $(error unknown operating system: $(uos)) endif ifeq ($(ucpu),i386) - mycpu = i386 + mycpu = i386 endif ifeq ($(ucpu),i486) - mycpu = i386 + mycpu = i386 endif ifeq ($(ucpu),i586) - mycpu = i386 + mycpu = i386 endif ifeq ($(ucpu),i686) - mycpu = i386 + mycpu = i386 endif ifeq ($(ucpu),bepc) - mycpu = i386 + mycpu = i386 endif ifeq ($(ucpu),i86pc) - mycpu = i386 + mycpu = i386 endif ifeq ($(ucpu),amd64) - mycpu = amd64 + mycpu = amd64 endif ifeq ($(ucpu),x86-64) - mycpu = amd64 + mycpu = amd64 endif ifeq ($(ucpu),x86_64) - mycpu = amd64 + mycpu = amd64 endif ifeq ($(ucpu),sparc) - mycpu = sparc + mycpu = sparc endif ifeq ($(ucpu),sun) - mycpu = sparc + mycpu = sparc endif ifeq ($(ucpu),ppc64) - mycpu = powerpc64 - ifeq ($(myos),linux) - COMP_FLAGS += -m64 - LINK_FLAGS += -m64 - endif + mycpu = powerpc64 + ifeq ($(myos),linux) + COMP_FLAGS += -m64 + LINK_FLAGS += -m64 + endif endif ifeq ($(ucpu),powerpc) - mycpu = powerpc + mycpu = powerpc endif ifeq ($(ucpu),ppc) - mycpu = ppc + mycpu = ppc endif ifeq ($(ucpu),mips) - mycpu = mips + mycpu = mips endif ifeq ($(ucpu),arm) - mycpu = arm + mycpu = arm endif ifeq ($(ucpu),armeb) - mycpu = arm + mycpu = arm endif ifeq ($(ucpu),armel) - mycpu = arm + mycpu = arm endif ifeq ($(ucpu),armv6l) - mycpu = arm + mycpu = arm endif ifndef ucpu - @echo "Error: unknown processor : $(ucpu)" - @exit 1 + $(error unknown processor: $(ucpu)) endif # for osA in 1..c.oses.len: ifeq ($(myos),?{c.oses[osA-1]}) # for cpuA in 1..c.cpus.len: - ifeq ($(mycpu),?{c.cpus[cpuA-1]}) + ifeq ($(mycpu),?{c.cpus[cpuA-1]}) # var oFiles = "" # for ff in c.cfiles[osA][cpuA].items: # oFiles.add(" " & changeFileExt(ff.toUnix, "o")) # end for - oFiles =?oFiles - endif + oFiles =?oFiles + endif # end for endif # end for ifeq ($(strip $(oFiles)),) - @echo "Error: no C code generated for: [$(myos): $(mycpu)]" - @exit 1 + $(error no C code generated for: [$(myos): $(mycpu)]) endif %.o: %.c 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) diff --git a/web/bountysource.nim b/web/bountysource.nim index 1d47cea56..5dfdb4497 100644 --- a/web/bountysource.nim +++ b/web/bountysource.nim @@ -33,13 +33,13 @@ proc getSupporters(self: BountySource): Future[JsonNode] {.async.} = let response = await self.client.get(apiUrl & "/supporters?order=monthly&per_page=200&team_slug=" & self.team) doAssert response.status.startsWith($Http200) - return parseJson(response.body) + return parseJson(await response.body) proc getGithubUser(username: string): Future[JsonNode] {.async.} = let client = newAsyncHttpClient() let response = await client.get(githubApiUrl & "/users/" & username) if response.status.startsWith($Http200): - return parseJson(response.body) + return parseJson(await response.body) else: echo("Could not get Github user: ", username, ". ", response.status) return nil diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 4f49bd8d9..3458ac83e 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -63,6 +63,10 @@ Changes affecting backwards compatibility compile-time value. - On posix, the results of `waitForExit`, `peekExitCode`, `execCmd` will return 128 + signal number if the application terminates via signal. +- ``ospaths.getConfigDir`` now conforms to the XDG Base Directory specification + on non-Windows OSs. It returns the value of the XDG_CONFIG_DIR environment + variable if it is set, and returns the default configuration directory, + "~/.config/", otherwise. Library Additions ----------------- @@ -200,3 +204,122 @@ via a commit, for a full list see - Fixed "await inside array/dict literal produces invalid code" (`#5314 <https://github.com/nim-lang/Nim/issues/5314>`_) +- Fixed "asyncdispatch.accept() can raise exception inside poll() instead of failing future on Windows" + (`#5279 <https://github.com/nim-lang/Nim/issues/5279>`_) +- Fixed "VM: A crash report should be more informative" + (`#5352 <https://github.com/nim-lang/Nim/issues/5352>`_) +- Fixed "IO routines are poor at handling errors" + (`#5349 <https://github.com/nim-lang/Nim/issues/5349>`_) +- Fixed "new import syntax doesn't work?" + (`#5185 <https://github.com/nim-lang/Nim/issues/5185>`_) +- Fixed "Seq of object literals skips unmentioned fields" + (`#5339 <https://github.com/nim-lang/Nim/issues/5339>`_) +- Fixed "``sym is not accessible`` in compile time" + (`#5354 <https://github.com/nim-lang/Nim/issues/5354>`_) +- Fixed "the matching is broken in re.nim" + (`#5382 <https://github.com/nim-lang/Nim/issues/5382>`_) +- Fixed "development branch breaks in my c wrapper" + (`#5392 <https://github.com/nim-lang/Nim/issues/5392>`_) +- Fixed "Bad codegen: toSeq + tuples + generics" + (`#5383 <https://github.com/nim-lang/Nim/issues/5383>`_) +- Fixed "Bad codegen: toSeq + tuples + generics" + (`#5383 <https://github.com/nim-lang/Nim/issues/5383>`_) +- Fixed "Codegen error when using container of containers" + (`#5402 <https://github.com/nim-lang/Nim/issues/5402>`_) +- Fixed "sizeof(RangeType) is not available in static context" + (`#5399 <https://github.com/nim-lang/Nim/issues/5399>`_) +- Fixed "Regression: ICE: expr: var not init ex_263713" + (`#5405 <https://github.com/nim-lang/Nim/issues/5405>`_) +- Fixed "Stack trace is wrong when assignment operator fails with template" + (`#5400 <https://github.com/nim-lang/Nim/issues/5400>`_) +- Fixed "SIGSEGV in compiler" + (`#5391 <https://github.com/nim-lang/Nim/issues/5391>`_) +- Fixed "Compiler regression with struct member names" + (`#5404 <https://github.com/nim-lang/Nim/issues/5404>`_) +- Fixed "Regression: compiler segfault" + (`#5419 <https://github.com/nim-lang/Nim/issues/5419>`_) +- Fixed "The compilation of jester routes is broken on devel" + (`#5417 <https://github.com/nim-lang/Nim/issues/5417>`_) +- Fixed "Non-generic return type produces "method is not a base"" + (`#5432 <https://github.com/nim-lang/Nim/issues/5432>`_) +- Fixed "Confusing error behavior when calling slice[T].random" + (`#5430 <https://github.com/nim-lang/Nim/issues/5430>`_) +- Fixed "Wrong method called" + (`#5439 <https://github.com/nim-lang/Nim/issues/5439>`_) +- Fixed "Attempt to document the strscans.scansp macro" + (`#5154 <https://github.com/nim-lang/Nim/issues/5154>`_) +- Fixed "[Regression] Invalid C code for _ symbol inside jester routes" + (`#5452 <https://github.com/nim-lang/Nim/issues/5452>`_) +- Fixed "StdLib base64 encodeInternal crashes with out of bound exception" + (`#5457 <https://github.com/nim-lang/Nim/issues/5457>`_) +- Fixed "Nim hangs forever in infinite loop in nre library" + (`#5444 <https://github.com/nim-lang/Nim/issues/5444>`_) + +- Fixed "Tester passes test although individual test in suite fails" + (`#5472 <https://github.com/nim-lang/Nim/issues/5472>`_) +- Fixed "terminal.nim documentation" + (`#5483 <https://github.com/nim-lang/Nim/issues/5483>`_) +- Fixed "Codegen error - expected identifier before ')' token (probably regression)" + (`#5481 <https://github.com/nim-lang/Nim/issues/5481>`_) +- Fixed "mixin not works inside generic proc generated by template" + (`#5478 <https://github.com/nim-lang/Nim/issues/5478>`_) +- Fixed "var not init (converter + template + macro)" + (`#5467 <https://github.com/nim-lang/Nim/issues/5467>`_) +- Fixed "`==` for OrderedTable should consider equal content but different size as equal." + (`#5487 <https://github.com/nim-lang/Nim/issues/5487>`_) +- Fixed "Fixed tests/tester.nim" + (`#45 <https://github.com/nim-lang/Nim/issues/45>`_) +- Fixed "template instanciation crashes compiler" + (`#5428 <https://github.com/nim-lang/Nim/issues/5428>`_) +- Fixed "Internal compiler error in handleGenericInvocation" + (`#5167 <https://github.com/nim-lang/Nim/issues/5167>`_) +- Fixed "compiler crash in forwarding template" + (`#5455 <https://github.com/nim-lang/Nim/issues/5455>`_) +- Fixed "Doc query re public/private + suggestion re deprecated" + (`#5529 <https://github.com/nim-lang/Nim/issues/5529>`_) +- Fixed "inheritance not work for generic object whose parent is parameterized" + (`#5264 <https://github.com/nim-lang/Nim/issues/5264>`_) +- Fixed "weird inheritance rule restriction" + (`#5231 <https://github.com/nim-lang/Nim/issues/5231>`_) +- Fixed "Enum with holes broken in JS" + (`#5062 <https://github.com/nim-lang/Nim/issues/5062>`_) +- Fixed "enum type and aliased enum type inequality when tested with operator `is` involving template" + (`#5360 <https://github.com/nim-lang/Nim/issues/5360>`_) +- Fixed "logging: problem with console logger caused by the latest changes in sysio" + (`#5546 <https://github.com/nim-lang/Nim/issues/5546>`_) +- Fixed "Crash if proc and caller doesn't define seq type - HEAD" + (`#4756 <https://github.com/nim-lang/Nim/issues/4756>`_) +- Fixed "`path` config option doesn't work when compilation is invoked from a different directory" + (`#5228 <https://github.com/nim-lang/Nim/issues/5228>`_) +- Fixed "segfaults module doesn't compile with C++ backend" + (`#5550 <https://github.com/nim-lang/Nim/issues/5550>`_) +- Fixed "Improve `joinThreads` for windows" + (`#4972 <https://github.com/nim-lang/Nim/issues/4972>`_) +- Fixed "Compiling in release mode prevents valid code execution." + (`#5296 <https://github.com/nim-lang/Nim/issues/5296>`_) +- Fixed "Forward declaration of generic procs or iterators doesn't work" + (`#4104 <https://github.com/nim-lang/Nim/issues/4104>`_) +- Fixed "cant create thread after join" + (`#4719 <https://github.com/nim-lang/Nim/issues/4719>`_) +- Fixed "can't compile with var name "near" and --threads:on" + (`#5598 <https://github.com/nim-lang/Nim/issues/5598>`_) +- Fixed "inconsistent behavior when calling parent's proc of generic object" + (`#5241 <https://github.com/nim-lang/Nim/issues/5241>`_) +- Fixed "The problem with import order of asyncdispatch and unittest modules" + (`#5597 <https://github.com/nim-lang/Nim/issues/5597>`_) +- Fixed "Generic code fails to compile in unexpected ways" + (`#976 <https://github.com/nim-lang/Nim/issues/976>`_) +- Fixed "Another 'User defined type class' issue" + (`#1128 <https://github.com/nim-lang/Nim/issues/1128>`_) +- Fixed "compiler fails to compile user defined typeclass" + (`#1147 <https://github.com/nim-lang/Nim/issues/1147>`_) +- Fixed "Type class membership testing doesn't work on instances of generic object types" + (`#1570 <https://github.com/nim-lang/Nim/issues/1570>`_) +- Fixed "Strange overload resolution behavior for procedures with typeclass arguments" + (`#1991 <https://github.com/nim-lang/Nim/issues/1991>`_) +- Fixed "The same UDTC can't constrain two type parameters in the same procedure" + (`#2018 <https://github.com/nim-lang/Nim/issues/2018>`_) +- Fixed "More trait/concept issues" + (`#2423 <https://github.com/nim-lang/Nim/issues/2423>`_) +- Fixed "Bugs with concepts?" + (`#2882 <https://github.com/nim-lang/Nim/issues/2882>`_) \ No newline at end of file |