diff options
Diffstat (limited to 'compiler')
38 files changed, 1068 insertions, 455 deletions
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) |