diff options
Diffstat (limited to 'compiler')
78 files changed, 2211 insertions, 1200 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index fecbefd7e..277a21ba5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -271,13 +271,14 @@ type TSymFlags* = set[TSymFlag] const - sfFakeConst* = sfDeadCodeElim # const cannot be put into a data section sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher sfNoInit* = sfMainModule # don't generate code to init the variable sfImmediate* = sfDeadCodeElim # macro or template is immediately expanded # without considering any possible overloads + sfAllUntyped* = sfVolatile # macro or template is immediately expanded \ + # in a generic context sfDirty* = sfPure # template is not hygienic (old styled template) @@ -395,6 +396,9 @@ type # sons[1]: field type # .n: nkDotExpr storing the field name + tyVoid #\ + # now different from tyEmpty, hurray! + static: # remind us when TTypeKind stops to fit in a single 64-bit word assert TTypeKind.high.ord <= 63 @@ -528,8 +532,6 @@ const tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \ # 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be # deprecated and this mess can be cleaned up. - tfVoid* = tfVarargs # for historical reasons we conflated 'void' with - # 'empty' ('@[]' has the type 'seq[empty]'). tfReturnsNew* = tfInheritable skError* = skUnknown @@ -544,7 +546,7 @@ type mEcho, mShallowCopy, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mUnaryLt, mInc, mDec, mOrd, - mNew, mNewFinalize, mNewSeq, + mNew, mNewFinalize, mNewSeq, mNewSeqOfCap, mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, mIncl, mExcl, mCard, mChr, @@ -563,8 +565,8 @@ type mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, - mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, - mXor, mEqProc, + mEqRef, mEqUntracedRef, mLePtr, mLtPtr, + mXor, mEqCString, mEqProc, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, @@ -590,6 +592,7 @@ type mNewString, mNewStringOfCap, mParseBiggestFloat, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, + mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, mInt, mInt8, mInt16, mInt32, mInt64, mUInt, mUInt8, mUInt16, mUInt32, mUInt64, @@ -609,7 +612,7 @@ type mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNGenSym, - mNimvm + mNimvm, mIntDefine, mStrDefine # things that we can evaluate safely at compile time, even if not asked for it: const @@ -769,7 +772,7 @@ type procInstCache*: seq[PInstantiation] gcUnsafetyReason*: PSym # for better error messages wrt gcsafe #scope*: PScope # the scope where the proc was defined - of skModule: + of skModule, skPackage: # modules keep track of the generic symbols they use from other modules. # this is because in incremental compilation, when a module is about to # be replaced with a newer version, we must decrement the usage count @@ -848,6 +851,7 @@ type # see instantiateDestructor in semdestruct.nim deepCopy*: PSym # overriden 'deepCopy' operation assignment*: PSym # overriden '=' operator + methods*: seq[(int,PSym)] # attached methods size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown align*: int16 # the type's alignment requirements @@ -938,7 +942,7 @@ const genericParamsPos* = 2 paramsPos* = 3 pragmasPos* = 4 - optimizedCodePos* = 5 # will be used for exception tracking + miscPos* = 5 # used for undocumented and hacky stuff bodyPos* = 6 # position of body; use rodread.getBody() instead! resultPos* = 7 dispatcherPos* = 8 # caution: if method has no 'result' it can be position 7! @@ -961,6 +965,8 @@ const skMethod, skConverter} var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things +var + gMainPackageId*: int proc isCallExpr*(n: PNode): bool = result = n.kind in nkCallKinds @@ -984,12 +990,12 @@ proc add*(father, son: PNode) = proc `[]`*(n: PNode, i: int): PNode {.inline.} = result = n.sons[i] -template `-|`*(b, s: expr): expr = +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): expr = n[i -| n] -template `{}=`*(n: PNode, i: int, s: PNode): stmt = +template `{}`*(n: PNode, i: int): untyped = n[i -| n] +template `{}=`*(n: PNode, i: int, s: PNode) = n.sons[i -| n] = s when defined(useNodeIds): @@ -1584,7 +1590,7 @@ proc isAtom*(n: PNode): bool {.inline.} = proc isEmptyType*(t: PType): bool {.inline.} = ## 'void' and 'stmt' types are often equivalent to 'nil' these days: - result = t == nil or t.kind in {tyEmpty, tyStmt} + result = t == nil or t.kind in {tyVoid, tyStmt} proc makeStmtList*(n: PNode): PNode = if n.kind == nkStmtList: diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index bd17f85e4..dffb8a9a5 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -124,7 +124,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] of tyArray, tyArrayConstr: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))] - else: + else: internalError("openArrayLoc: " & typeToString(a.t)) else: internalError("openArrayLoc: " & typeToString(a.t)) @@ -260,7 +260,11 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = else: result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) else: - result = genArgNoParam(p, ri.sons[i]) + if tfVarargs notin typ.flags: + localError(ri.info, "wrong argument count") + result = nil + else: + result = genArgNoParam(p, ri.sons[i]) discard """ Dot call syntax in C++ diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 4fcbeeec2..6c96f209e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -45,7 +45,7 @@ proc genHexLiteral(v: PNode): Rope = proc getStrLit(m: BModule, s: string): Rope = discard cgsym(m, "TGenericSeq") - result = "TMP" & rope(backendId()) + result = getTempName(m) addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", [result, makeCString(s), rope(len(s))]) @@ -67,11 +67,11 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = of nkNilLit: let t = skipTypes(ty, abstractVarRange) if t.kind == tyProc and t.callConv == ccClosure: - var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - result = "TMP" & rope(id) - if id == gBackendId: + let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + result = p.module.tmpBase & rope(id) + if id == p.module.labels: # not found in cache: - inc(gBackendId) + inc(p.module.labels) addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n", [getTypeDesc(p.module, t), result]) @@ -81,13 +81,14 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = if n.strVal.isNil: result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) elif skipTypes(ty, abstractVarRange).kind == tyString: - var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - if id == gBackendId: + let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + if id == p.module.labels: # string literal not found in the cache: result = ropecg(p.module, "((#NimStringDesc*) &$1)", [getStrLit(p.module, n.strVal)]) else: - result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [rope(id)]) + result = ropecg(p.module, "((#NimStringDesc*) &$1$2)", + [p.module.tmpBase, rope(id)]) else: result = makeCString(n.strVal) of nkFloatLit..nkFloat64Lit: @@ -134,11 +135,11 @@ proc genSetNode(p: BProc, n: PNode): Rope = var size = int(getSize(n.typ)) toBitSet(n, cs) if size > 8: - var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - result = "TMP" & rope(id) - if id == gBackendId: + let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + result = p.module.tmpBase & rope(id) + if id == p.module.labels: # not found in cache: - inc(gBackendId) + inc(p.module.labels) addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)]) else: @@ -490,7 +491,7 @@ proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; var size = getSize(t) let storage = if size < platform.intSize: rope("NI") else: getTypeDesc(p.module, t) - result = getTempName() + result = getTempName(p.module) linefmt(p, cpsLocals, "$1 $2;$n", storage, result) lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b)) if size < platform.intSize or t.kind in {tyRange, tyEnum}: @@ -591,7 +592,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "($1 == $2)", # EqPtr "($1 <= $2)", # LePtr "($1 < $2)", # LtPtr - "($1 == $2)", # EqCString "($1 != $2)"] # Xor var a, b: TLoc @@ -612,7 +612,7 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) = assert(e.sons[2].typ != nil) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - if a.t.callConv == ccClosure: + if a.t.skipTypes(abstractInst).callConv == ccClosure: putIntoDest(p, d, e.typ, "($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)" % [rdLoc(a), rdLoc(b)]) else: @@ -670,6 +670,8 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = #if e[0].kind != nkBracketExpr: # message(e.info, warnUser, "CAME HERE " & renderTree(e)) expr(p, e.sons[0], d) + if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef: + d.s = OnHeap else: var a: TLoc let typ = skipTypes(e.sons[0].typ, abstractInst) @@ -802,9 +804,9 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym; v.r.add(d.loc.r) genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, - newStrNode(nkStrLit, field.name.s), gBackendId) - let strLit = if id == gBackendId: getStrLit(p.module, field.name.s) - else: "TMP" & rope(id) + newStrNode(nkStrLit, field.name.s), p.module.labels) + let strLit = if id == p.module.labels: getStrLit(p.module, field.name.s) + else: p.module.tmpBase & rope(id) if op.magic == mNot: linefmt(p, cpsStmts, "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n", @@ -1141,7 +1143,35 @@ proc genNewSeq(p: BProc, e: PNode) = genNewSeqAux(p, a, b.rdLoc) gcUsage(e) +proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = + let seqtype = skipTypes(e.typ, abstractVarRange) + var a: TLoc + initLocExpr(p, e.sons[1], a) + putIntoDest(p, d, e.typ, ropecg(p.module, + "($1)#nimNewSeqOfCap($2, $3)", [ + getTypeDesc(p.module, seqtype), + genTypeInfo(p.module, seqtype), a.rdLoc])) + gcUsage(e) + +proc genConstExpr(p: BProc, n: PNode): Rope +proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = + if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: + var t = getUniqueType(n.typ) + discard getTypeDesc(p.module, t) # so that any fields are initialized + let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + fillLoc(d, locData, t, p.module.tmpBase & rope(id), OnStatic) + if id == p.module.labels: + # expression not found in the cache: + inc(p.module.labels) + addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)]) + result = true + else: + result = false + proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = + if handleConstExpr(p, e, d): return + #echo rendertree e, " ", e.isDeepConstExpr var tmp: TLoc var t = e.typ.skipTypes(abstractInst) getTemp(p, t, tmp) @@ -1235,7 +1265,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope): Rope = # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we # have to call it here first: let ti = genTypeInfo(p.module, dest) - if tfFinal in dest.flags or (p.module.objHasKidsValid and + if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and tfObjHasKids notin dest.flags): result = "$1.m_type == $2" % [a, ti] else: @@ -1293,7 +1323,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s) of tyEnum, tyOrdinal: putIntoDest(p, d, e.typ, - ropecg(p.module, "#reprEnum($1, $2)", [ + ropecg(p.module, "#reprEnum((NI)$1, $2)", [ rdLoc(a), genTypeInfo(p.module, t)]), a.s) of tyString: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s) @@ -1320,7 +1350,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", [ rdLoc(a), genTypeInfo(p.module, t)]), a.s) - of tyEmpty: + of tyEmpty, tyVoid: localError(e.info, "'repr' doesn't support 'void' type") else: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", @@ -1558,11 +1588,12 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s) proc genCast(p: BProc, e: PNode, d: var TLoc) = - const floatTypes = {tyFloat..tyFloat128} + const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, + tyArray, tyArrayConstr} let destt = skipTypes(e.typ, abstractRange) srct = skipTypes(e.sons[1].typ, abstractRange) - if destt.kind in floatTypes or srct.kind in floatTypes: + if destt.kind in ValueTypes or srct.kind in ValueTypes: # 'cast' and some float type involved? --> use a union. inc(p.labels) var lbl = p.labels.rope @@ -1668,10 +1699,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)") of mInc, mDec: - const opr: array [mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"] - const fun64: array [mInc..mDec, string] = ["$# = #addInt64($#, $#);$n", + const opr: array[mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"] + const fun64: array[mInc..mDec, string] = ["$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"] - const fun: array [mInc..mDec, string] = ["$# = #addInt($#, $#);$n", + const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar, tyRange}) if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: @@ -1712,6 +1743,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNew: genNew(p, e) of mNewFinalize: genNewFinalize(p, e) of mNewSeq: genNewSeq(p, e) + of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: let t = e.sons[1].typ.skipTypes({tyTypeDesc}) putIntoDest(p, d, e.typ, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) @@ -1754,25 +1786,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, x, a) initLocExpr(p, e.sons[2], b) genDeepCopy(p, a, b) - of mDotDot: genCall(p, e, d) + of mDotDot, mEqCString: genCall(p, e, d) else: internalError(e.info, "genMagicExpr: " & $op) -proc genConstExpr(p: BProc, n: PNode): Rope -proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = - if nfAllConst in n.flags and d.k == locNone and n.len > 0 and n.isDeepConstExpr: - var t = getUniqueType(n.typ) - discard getTypeDesc(p.module, t) # so that any fields are initialized - var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - fillLoc(d, locData, t, "TMP" & rope(id), OnStatic) - if id == gBackendId: - # expression not found in the cache: - inc(gBackendId) - addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)]) - result = true - else: - result = false - proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # example: { a..b, c, d, e, f..g } # we have to emit an expression of the form: @@ -1856,10 +1872,16 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = initLocExpr(p, n.sons[1], b) if n.sons[0].skipConv.kind == nkClosure: internalError(n.info, "closure to closure created") - getTemp(p, n.typ, tmp) - linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n", - tmp.rdLoc, a.rdLoc, b.rdLoc) - putLocIntoDest(p, d, tmp) + # tasyncawait.nim breaks with this optimization: + when false: + if d.k != locNone: + linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n", + d.rdLoc, a.rdLoc, b.rdLoc) + else: + getTemp(p, n.typ, tmp) + linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n", + tmp.rdLoc, a.rdLoc, b.rdLoc) + putLocIntoDest(p, d, tmp) proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = var arr: TLoc @@ -1947,12 +1969,12 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = var t = getUniqueType(n.typ) discard getTypeDesc(p.module, t) # so that any fields are initialized - var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - var tmp = "TMP" & rope(id) + let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + let tmp = p.module.tmpBase & rope(id) - if id == gBackendId: + if id == p.module.labels: # expression not found in the cache: - inc(gBackendId) + inc(p.module.labels) addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) @@ -1960,6 +1982,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = fillLoc(d, locData, t, tmp, OnStatic) else: putDataIntoDest(p, d, t, tmp) + # This fixes bug #4551, but we really need better dataflow + # analysis to make this 100% safe. + if t.kind notin {tySequence, tyString}: + d.s = OnStatic proc expr(p: BProc, n: PNode, d: var TLoc) = case n.kind @@ -1985,10 +2011,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = internalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: - if sfFakeConst in sym.flags: - if sfGlobal in sym.flags: genVarPrototype(p.module, sym) - putLocIntoDest(p, d, sym.loc) - elif isSimpleConst(sym.typ): + if isSimpleConst(sym.typ): putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic) else: genComplexConst(p, sym, d) @@ -2160,7 +2183,8 @@ proc genConstSimpleList(p: BProc, n: PNode): Rope = result = rope("{") for i in countup(ord(n.kind == nkObjConstr), length - 2): addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])]) - if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1])) + if length > ord(n.kind == nkObjConstr): + add(result, genNamedConstExpr(p, n.sons[length - 1])) addf(result, "}$n", []) proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = @@ -2174,8 +2198,7 @@ proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = data.add("}") data.add("}") - inc(gBackendId) - result = "CNSTSEQ" & gBackendId.rope + result = getTempName(p.module) appcg(p.module, cfsData, "NIM_CONST struct {$n" & diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 2a37257b6..92b6aa9dc 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -107,13 +107,13 @@ proc genMergeInfo*(m: BModule): Rope = writeIntSet(m.typeInfoMarker, s) s.add("labels:") encodeVInt(m.labels, s) - s.add(" hasframe:") - encodeVInt(ord(m.frameDeclared), s) + s.add(" flags:") + encodeVInt(cast[int](m.flags), s) s.add(tnl) s.add("*/") result = s.rope -template `^`(pos: int): expr = L.buf[pos] +template `^`(pos: int): untyped = L.buf[pos] proc skipWhite(L: var TBaseLexer) = var pos = L.bufpos @@ -222,13 +222,14 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) = of "declared": readIntSet(L, m.declaredThings) of "typeInfo": readIntSet(L, m.typeInfoMarker) of "labels": m.labels = decodeVInt(L.buf, L.bufpos) - of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0 + of "flags": + m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0) else: internalError("ccgmerge: unknown key: " & k) when not defined(nimhygiene): {.pragma: inject.} -template withCFile(cfilename: string, body: stmt) {.immediate.} = +template withCFile(cfilename: string, body: untyped) = var s = llStreamOpen(cfilename, fmRead) if s == nil: return var L {.inject.}: TBaseLexer diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f2ceadcce..a5ce147c3 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -16,7 +16,7 @@ const # above X strings a hash-switch for strings is generated proc registerGcRoot(p: BProc, v: PSym) = - if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2} and + if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) @@ -136,7 +136,7 @@ proc exprBlock(p: BProc, n: PNode, d: var TLoc) = expr(p, n, d) endBlock(p) -template preserveBreakIdx(body: stmt): stmt {.immediate.} = +template preserveBreakIdx(body: untyped): untyped = var oldBreakIdx = p.breakIdx body p.breakIdx = oldBreakIdx @@ -269,8 +269,6 @@ proc genConstStmt(p: BProc, t: PNode) = if it.kind != nkConstDef: internalError(t.info, "genConstStmt") var c = it.sons[0].sym if c.typ.containsCompileTimeOnly: continue - if sfFakeConst in c.flags: - genSingleVar(p, it) elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and c.ast.len != 0: if not emitLazily(c): requestConstImpl(p, c) @@ -570,7 +568,7 @@ proc genRaiseStmt(p: BProc, t: PNode) = else: genLineDir(p, t) # reraise the last exception: - if p.module.compileToCpp: + if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: line(p, cpsStmts, ~"throw;$n") else: linefmt(p, cpsStmts, "#reraiseException();$n") @@ -793,7 +791,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) genLineDir(p, t) - let exc = getTempName() + let exc = getTempName(p.module) if getCompilerProc("Exception") != nil: discard cgsym(p.module, "Exception") else: @@ -886,7 +884,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = getTemp(p, t.typ, d) discard lists.includeStr(p.module.headerFiles, "<setjmp.h>") genLineDir(p, t) - var safePoint = getTempName() + var safePoint = getTempName(p.module) if getCompilerProc("Exception") != nil: discard cgsym(p.module, "Exception") else: @@ -961,6 +959,8 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = var a: TLoc initLocExpr(p, t.sons[i], a) res.add($rdLoc(a)) + elif sym.kind == skType: + res.add($getTypeDesc(p.module, sym.typ)) else: var r = sym.loc.r if r == nil: diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index ab771d240..81af89249 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -18,7 +18,7 @@ proc emulatedThreadVars(): bool = proc accessThreadLocalVar(p: BProc, s: PSym) = if emulatedThreadVars() and not p.threadVarAccessed: p.threadVarAccessed = true - p.module.usesThreadVars = true + incl p.module.flags, usesThreadVars addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", []) add(p.procSec(cpsInit), ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n")) @@ -51,7 +51,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = addf(m.s[cfsVars], " $1;$n", [s.loc.r]) proc generateThreadLocalStorage(m: BModule) = - if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags): + if nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags): for t in items(nimtvDeps): discard getTypeDesc(m, t) addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv]) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 0da6396ea..c4d47f148 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -100,7 +100,7 @@ proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) = proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope = var c: TTraversalClosure var p = newProc(nil, m) - result = getGlobalTempName() + result = getTempName(m) case reason of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" @@ -135,7 +135,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope = var c: TTraversalClosure var p = newProc(nil, m) var sLoc = s.loc.r - result = getGlobalTempName() + result = getTempName(m) if sfThread in s.flags and emulatedThreadVars(): accessThreadLocalVar(p, s) @@ -143,7 +143,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope = c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n" c.p = p - let header = "N_NIMCALL(void, $1)()" % [result] + let header = "N_NIMCALL(void, $1)(void)" % [result] genTraverseProc(c, sLoc, s.loc.t) let generatedProc = "$1 {$n$2$3$4}$n" % diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 39f16ff0d..3d1c2affc 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2016 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -11,6 +11,8 @@ # ------------------------- Name Mangling -------------------------------- +import debuginfo + proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords # it's more efficient to test the whole Nim keywords range @@ -23,70 +25,70 @@ proc isKeyword(w: PIdent): bool = proc mangleField(name: PIdent): string = result = mangle(name.s) if isKeyword(name): - result[0] = result[0].toUpper # Mangling makes everything lowercase, - # but some identifiers are C keywords + result[0] = result[0].toUpperAscii + # Mangling makes everything lowercase, + # but some identifiers are C keywords + +proc hashOwner(s: PSym): FilenameHash = + var m = s + while m.kind != skModule: m = m.owner + let p = m.owner + assert p.kind == skPackage + result = gDebugInfo.register(p.name.s, m.name.s) proc mangleName(s: PSym): Rope = result = s.loc.r if result == nil: - when oKeepVariableNames: - let keepOrigName = s.kind in skLocalVars - {skForVar} and - {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and - not isKeyword(s.name) - # XXX: This is still very experimental - # - # Even with all these inefficient checks, the bootstrap - # time is actually improved. This is probably because so many - # rope concatenations are now eliminated. - # - # Future notes: - # sfFromGeneric seems to be needed in order to avoid multiple - # definitions of certain variables generated in transf with - # names such as: - # `r`, `res` - # I need to study where these come from. - # - # about sfShadowed: - # consider the following Nim code: - # var x = 10 - # block: - # var x = something(x) - # The generated C code will be: - # NI x; - # x = 10; - # { - # NI x; - # x = something(x); // Oops, x is already shadowed here - # } - # Right now, we work-around by not keeping the original name - # of the shadowed variable, but we can do better - we can - # create an alternative reference to it in the outer scope and - # use that in the inner scope. - # - # about isCKeyword: - # Nim variable names can be C keywords. - # We need to avoid such names in the generated code. - # XXX: Study whether mangleName is called just once per variable. - # Otherwise, there might be better place to do this. - # - # about sfGlobal: - # This seems to be harder - a top level extern variable from - # another modules can have the same name as a local one. - # Maybe we should just implement sfShadowed for them too. - # - # about skForVar: - # These are not properly scoped now - we need to add blocks - # around for loops in transf - if keepOrigName: - result = s.name.s.mangle.rope - else: - add(result, rope(mangle(s.name.s))) - add(result, ~"_") - add(result, rope(s.id)) + let keepOrigName = s.kind in skLocalVars - {skForVar} and + {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and + not isKeyword(s.name) + # Even with all these inefficient checks, the bootstrap + # time is actually improved. This is probably because so many + # rope concatenations are now eliminated. + # + # sfFromGeneric is needed in order to avoid multiple + # definitions of certain variables generated in transf with + # names such as: + # `r`, `res` + # I need to study where these come from. + # + # about sfShadowed: + # consider the following Nim code: + # var x = 10 + # block: + # var x = something(x) + # The generated C code will be: + # NI x; + # x = 10; + # { + # NI x; + # x = something(x); // Oops, x is already shadowed here + # } + # Right now, we work-around by not keeping the original name + # of the shadowed variable, but we can do better - we can + # create an alternative reference to it in the outer scope and + # use that in the inner scope. + # + # about isCKeyword: + # Nim variable names can be C keywords. + # We need to avoid such names in the generated code. + # + # about sfGlobal: + # This seems to be harder - a top level extern variable from + # another modules can have the same name as a local one. + # Maybe we should just implement sfShadowed for them too. + # + # about skForVar: + # These are not properly scoped now - we need to add blocks + # around for loops in transf + result = s.name.s.mangle.rope + if keepOrigName: + result.add "0" else: - add(result, rope(mangle(s.name.s))) add(result, ~"_") add(result, rope(s.id)) + add(result, ~"_") + add(result, rope(hashOwner(s).BiggestInt)) s.loc.r = result proc typeName(typ: PType): Rope = @@ -137,6 +139,10 @@ proc mapType(typ: PType): TCTypeKind = var base = skipTypes(typ.lastSon, typedescInst) case base.kind of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray + #of tySet: + # if mapSetType(base) == ctArray: result = ctPtrToArray + # else: result = ctPtr + # XXX for some reason this breaks the pegs module else: result = ctPtr of tyPointer: result = ctPtr of tySequence: result = ctNimSeq @@ -145,11 +151,15 @@ proc mapType(typ: PType): TCTypeKind = of tyCString: result = ctCString of tyInt..tyUInt64: result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) + of tyStatic: + if typ.n != nil: result = mapType(lastSon typ) + else: internalError("mapType") else: internalError("mapType") proc mapReturnType(typ: PType): TCTypeKind = - if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr - else: result = mapType(typ) + #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr + #else: + result = mapType(typ) proc isImportedType(t: PType): bool = result = t.sym != nil and sfImportc in t.sym.flags @@ -196,11 +206,9 @@ proc cacheGetType(tab: TIdTable, key: PType): Rope = # linear search is not necessary anymore: result = Rope(idTableGet(tab, key)) -proc getTempName(): Rope = - result = rfmt(nil, "TMP$1", rope(backendId())) - -proc getGlobalTempName(): Rope = - result = rfmt(nil, "TMP$1", rope(backendId())) +proc getTempName(m: BModule): Rope = + result = m.tmpBase & rope(m.labels) + inc m.labels proc ccgIntroducedPtr(s: PSym): bool = var pt = skipTypes(s.typ, typedescInst) @@ -223,7 +231,7 @@ proc ccgIntroducedPtr(s: PSym): bool = proc fillResult(param: PSym) = fillLoc(param.loc, locParam, param.typ, ~"Result", OnStack) - if (mapReturnType(param.typ) != ctArray) and isInvalidReturnType(param.typ): + if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ): incl(param.loc.flags, lfIndirect) param.loc.s = OnUnknown @@ -238,22 +246,10 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = NumericalTypeToStr: array[tyInt..tyUInt64, string] = [ "NI", "NI8", "NI16", "NI32", "NI64", "NF", "NF32", "NF64", "NF128", - "NU", "NU8", "NU16", "NU32", "NU64",] + "NU", "NU8", "NU16", "NU32", "NU64"] case typ.kind of tyPointer: result = typeNameOrLiteral(typ, "void*") - of tyEnum: - if firstOrd(typ) < 0: - result = typeNameOrLiteral(typ, "NI32") - else: - case int(getSize(typ)) - of 1: result = typeNameOrLiteral(typ, "NU8") - of 2: result = typeNameOrLiteral(typ, "NU16") - of 4: result = typeNameOrLiteral(typ, "NI32") - of 8: result = typeNameOrLiteral(typ, "NI64") - else: - internalError(typ.sym.info, "getSimpleTypeDesc: " & $(getSize(typ))) - result = nil of tyString: discard cgsym(m, "NimStringDesc") result = typeNameOrLiteral(typ, "NimStringDesc*") @@ -264,6 +260,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyInt..tyUInt64: result = typeNameOrLiteral(typ, NumericalTypeToStr[typ.kind]) of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0]) + of tyStatic: + if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ) + else: internalError("tyStatic for getSimpleTypeDesc") + of tyGenericInst: + result = getSimpleTypeDesc(m, lastSon typ) else: result = nil proc pushType(m: BModule, typ: PType) = @@ -317,7 +318,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeDescAux(m, t, check) proc paramStorageLoc(param: PSym): TStorageLoc = - if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray}: + if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray, tyVarargs, tyArrayConstr}: result = OnStack else: result = OnUnknown @@ -326,7 +327,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, check: var IntSet, declareEnvironment=true; weakDep=false) = params = nil - if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]): + if t.sons[0] == nil or isInvalidReturnType(t.sons[0]): rettype = ~"void" else: rettype = getTypeDescAux(m, t.sons[0], check) @@ -359,10 +360,10 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, addf(params, ", NI $1Len$2", [param.loc.r, j.rope]) inc(j) arr = arr.sons[0] - if (t.sons[0] != nil) and isInvalidReturnType(t.sons[0]): + if t.sons[0] != nil and isInvalidReturnType(t.sons[0]): var arr = t.sons[0] if params != nil: add(params, ", ") - if (mapReturnType(t.sons[0]) != ctArray): + if mapReturnType(t.sons[0]) != ctArray: add(params, getTypeDescWeak(m, arr, check)) add(params, "*") else: @@ -424,7 +425,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, addf(result, "union{$n$1} $2;$n", [unionBody, uname]) of nkSym: field = n.sym - if field.typ.kind == tyEmpty: return + if field.typ.kind == tyVoid: return #assert(field.ast == nil) sname = mangleRecFieldName(field, rectype) if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname] @@ -483,7 +484,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, else: addf(result, " {$n", [name]) - var desc = getRecordFields(m, typ, check) + let desc = getRecordFields(m, typ, check) if desc == nil and not hasField: addf(result, "char dummy;$n", []) else: @@ -536,8 +537,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = result = getTypePre(m, t) if result != nil: return if containsOrIncl(check, t.id): - if isImportedCppType(typ) or isImportedCppType(t): return - internalError("cannot generate C type for: " & typeToString(typ)) + if not (isImportedCppType(typ) or isImportedCppType(t)): + internalError("cannot generate C type for: " & typeToString(typ)) # XXX: this BUG is hard to fix -> we need to introduce helper structs, # but determining when this needs to be done is hard. We should split # C type generation into an analysis and a code generation phase somehow. @@ -576,8 +577,37 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = result = getTypeDescAux(m, et, check) & star idTablePut(m.typeCache, t, result) of tyOpenArray, tyVarargs: - result = getTypeDescAux(m, t.sons[0], check) & "*" + result = getTypeDescWeak(m, t.sons[0], check) & "*" idTablePut(m.typeCache, t, result) + of tyRange, tyEnum: + let t = if t.kind == tyRange: t.lastSon else: t + result = cacheGetType(m.typeCache, t) + if result == nil: + result = getTypeName(t) + if not (isImportedCppType(t) or + (sfImportc in t.sym.flags and t.sym.magic == mNone)): + idTablePut(m.typeCache, t, result) + var size: int + if firstOrd(t) < 0: + addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) + size = 4 + else: + size = int(getSize(t)) + case size + of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result]) + of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result]) + of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) + of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) + else: internalError(t.sym.info, "getTypeDescAux: enum") + let owner = hashOwner(t.sym) + if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner): + var vals: seq[(string, int)] = @[] + for i in countup(0, t.n.len - 1): + assert(t.n.sons[i].kind == nkSym) + let field = t.n.sons[i].sym + vals.add((field.name.s, field.position.int)) + gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id, + name: t.sym.name.s, values: vals)) of tyProc: result = getTypeName(t) idTablePut(m.typeCache, t, result) @@ -642,7 +672,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = chunkStart = i let typeInSlot = resolveStarsInCppType(typ, idx + 1, stars) - if typeInSlot == nil or typeInSlot.kind == tyEmpty: + if typeInSlot == nil or typeInSlot.kind == tyVoid: result.add(~"void") else: result.add getTypeDescAux(m, typeInSlot, check) @@ -654,7 +684,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = else: result = cppName & "<" for i in 1 .. typ.len-2: - if i > 1: result.add(", ") + if i > 1: result.add(" COMMA ") result.add(getTypeDescAux(m, typ.sons[i], check)) result.add("> ") # always call for sideeffects: @@ -673,16 +703,13 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = else: getTupleDesc(m, t, result, check) if not isImportedType(t): add(m.s[cfsTypes], recdesc) of tySet: - case int(getSize(t)) - of 1: result = rope("NU8") - of 2: result = rope("NU16") - of 4: result = rope("NU32") - of 8: result = rope("NU64") - else: - result = getTypeName(t) - idTablePut(m.typeCache, t, result) - if not isImportedType(t): - addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", + result = getTypeName(t.lastSon) & "Set" + idTablePut(m.typeCache, t, result) + if not isImportedType(t): + let s = int(getSize(t)) + case s + of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)]) + else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", [result, rope(getSize(t))]) of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyTypeDesc: @@ -704,7 +731,7 @@ type proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope = assert t.kind == tyProc var check = initIntSet() - result = getTempName() + result = getTempName(m) var rettype, desc: Rope genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf) if not isImportedType(t): @@ -739,7 +766,7 @@ proc genProcHeader(m: BModule, prc: PSym): Rope = genCLineDir(result, prc.info) # using static is needed for inline procs if lfExportLib in prc.loc.flags: - if m.isHeaderFile: + if isHeaderFile in m.flags: result.add "N_LIB_IMPORT " else: result.add "N_LIB_EXPORT " @@ -819,7 +846,7 @@ proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: Rope) = if L == 1: genObjectFields(m, typ, n.sons[0], expr) elif L > 0: - var tmp = getTempName() + var tmp = getTempName(m) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)]) for i in countup(0, L-1): var tmp2 = getNimNode(m) @@ -891,7 +918,7 @@ proc genTupleInfo(m: BModule, typ: PType, name: Rope) = var expr = getNimNode(m) var length = sonsLen(typ) if length > 0: - var tmp = getTempName() + var tmp = getTempName(m) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(length)]) for i in countup(0, length - 1): var a = typ.sons[i] @@ -915,7 +942,7 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope) = # anyway. We generate a cstring array and a loop over it. Exceptional # positions will be reset after the loop. genTypeInfoAux(m, typ, typ, name) - var nodePtrs = getTempName() + var nodePtrs = getTempName(m) var length = sonsLen(typ.n) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [nodePtrs, rope(length)]) @@ -935,8 +962,8 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope) = if field.position != i or tfEnumHasHoles in typ.flags: addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)]) hasHoles = true - var enumArray = getTempName() - var counter = getTempName() + var enumArray = getTempName(m) + var counter = getTempName(m) addf(m.s[cfsTypeInit1], "NI $1;$n", [counter]) addf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n", [enumArray, rope(length), enumNames]) @@ -1004,9 +1031,12 @@ proc genTypeInfo(m: BModule, t: PType): Rope = [result, rope(typeToString(t))]) return "(&".rope & result & ")".rope case t.kind - of tyEmpty: result = rope"0" + of tyEmpty, tyVoid: result = rope"0" of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar: genTypeInfoAuxBase(m, t, t, result, rope"0") + of tyStatic: + if t.n != nil: result = genTypeInfo(m, lastSon t) + else: internalError("genTypeInfo(" & $t.kind & ')') of tyProc: if t.callConv != ccClosure: genTypeInfoAuxBase(m, t, t, result, rope"0") diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 6dfd7b52c..ecd98a2bf 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -93,7 +93,7 @@ proc getUniqueType*(key: PType): PType = # produced instead of ``NI``. result = key of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString, - tyCString, tyNone, tyBigNum: + tyCString, tyNone, tyBigNum, tyVoid: result = gCanonicalTypes[k] if result == nil: gCanonicalTypes[k] = key @@ -188,7 +188,7 @@ proc mangle*(name: string): string = let c = name[i] case c of 'A'..'Z': - add(result, c.toLower) + add(result, c.toLowerAscii) of '_': discard of 'a'..'z', '0'..'9': diff --git a/compiler/cgen.nim b/compiler/cgen.nim index db376821c..9851ab0e2 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -64,8 +64,8 @@ proc isSimpleConst(typ: PType): bool = (t.kind == tyProc and t.callConv == ccClosure) proc useStringh(m: BModule) = - if not m.includesStringh: - m.includesStringh = true + if includesStringh notin m.flags: + incl m.flags, includesStringh discard lists.includeStr(m.headerFiles, "<string.h>") proc useHeader(m: BModule, sym: PSym) = @@ -129,7 +129,7 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope = if i - 1 >= start: add(result, substr(frmt, start, i - 1)) -template rfmt(m: BModule, fmt: string, args: varargs[Rope]): expr = +template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped = ropecg(m, fmt, args) proc appcg(m: BModule, c: var Rope, frmt: FormatStr, @@ -215,7 +215,7 @@ proc accessThreadLocalVar(p: BProc, s: PSym) proc emulatedThreadVars(): bool {.inline.} proc genProc(m: BModule, prc: PSym) -template compileToCpp(m: BModule): expr = +template compileToCpp(m: BModule): untyped = gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags include "ccgtypes.nim" @@ -301,7 +301,8 @@ proc resetLoc(p: BProc, loc: var TLoc) = proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = let typ = skipTypes(loc.t, abstractRange) if not isComplexValueType(typ): - linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) + linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc), + getTypeDesc(p.module, typ)) else: if not isTemp or containsGarbageCollectedRef(loc.t): # don't use memset for temporary values for performance if we can @@ -327,10 +328,12 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = inc(p.labels) result.r = "LOC" & rope(p.labels) - linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) + addf(p.blocks[0].sections[cpsLocals], + "$1 $2;$n", [getTypeDesc(p.module, t), result.r]) result.k = locTemp #result.a = - 1 - result.t = getUniqueType(t) + result.t = t + #result.t = getUniqueType(t) result.s = OnStack result.flags = {} constructLoc(p, result, not needsInit) @@ -498,7 +501,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = assert(lib != nil) if not lib.generated: lib.generated = true - var tmp = getGlobalTempName() + var tmp = getTempName(m) assert(lib.name == nil) lib.name = tmp # BUGFIX: cgsym has awful side-effects addf(m.s[cfsVars], "static void* $1;$n", [tmp]) @@ -666,7 +669,7 @@ proc genProcAux(m: BModule, prc: PSym) = fillResult(res) assignParam(p, res) if skipTypes(res.typ, abstractInst).kind == tyArray: - incl(res.loc.flags, lfIndirect) + #incl(res.loc.flags, lfIndirect) res.loc.s = OnUnknown for i in countup(1, sonsLen(prc.typ.n) - 1): @@ -1009,11 +1012,11 @@ proc genInitCode(m: BModule) = add(prc, m.postInitProc.s(cpsLocals)) add(prc, genSectionEnd(cpsLocals)) - if optStackTrace in m.initProc.options and not m.frameDeclared: + if optStackTrace in m.initProc.options and frameDeclared notin m.flags: # BUT: the generated init code might depend on a current frame, so # declare it nevertheless: - m.frameDeclared = true - if not m.preventStackTrace: + incl m.flags, frameDeclared + if preventStackTrace notin m.flags: var procname = makeCString(m.module.name.s) add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) else: @@ -1030,7 +1033,7 @@ proc genInitCode(m: BModule) = add(prc, m.initProc.s(cpsStmts)) add(prc, m.postInitProc.s(cpsStmts)) add(prc, genSectionEnd(cpsStmts)) - if optStackTrace in m.initProc.options and not m.preventStackTrace: + if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: add(prc, deinitFrame(m.initProc)) add(prc, deinitGCFrame(m.initProc)) addf(prc, "}$N$N", []) @@ -1059,9 +1062,8 @@ proc genModule(m: BModule, cfile: string): Rope = result = getFileHeader(cfile) result.add(genMergeInfo(m)) - generateHeaders(m) - generateThreadLocalStorage(m) + generateHeaders(m) for i in countup(cfsHeaders, cfsProcs): add(result, genSectionStart(i)) add(result, m.s[i]) @@ -1083,6 +1085,7 @@ proc initProcOptions(m: BModule): TOptions = proc rawNewModule(module: PSym, filename: string): BModule = new(result) + result.tmpBase = rope("T" & $hashOwner(module) & "_") initLinkedList(result.headerFiles) result.declaredThings = initIntSet() result.declaredProtos = initIntSet() @@ -1099,12 +1102,12 @@ proc rawNewModule(module: PSym, filename: string): BModule = initNodeTable(result.dataCache) result.typeStack = @[] result.forwardedProcs = @[] - result.typeNodesName = getTempName() - result.nimTypesName = getTempName() + result.typeNodesName = getTempName(result) + result.nimTypesName = getTempName(result) # no line tracing for the init sections of the system module so that we # don't generate a TFrame which can confuse the stack botton initialization: if sfSystemModule in module.flags: - result.preventStackTrace = true + incl result.flags, preventStackTrace excl(result.preInitProc.options, optStackTrace) excl(result.postInitProc.options, optStackTrace) @@ -1125,11 +1128,13 @@ proc resetModule*(m: BModule) = initNodeTable(m.dataCache) m.typeStack = @[] m.forwardedProcs = @[] - m.typeNodesName = getTempName() - m.nimTypesName = getTempName() - m.preventStackTrace = sfSystemModule in m.module.flags + m.typeNodesName = getTempName(m) + m.nimTypesName = getTempName(m) + if sfSystemModule in m.module.flags: + incl m.flags, preventStackTrace + else: + excl m.flags, preventStackTrace nullify m.s - m.usesThreadVars = false m.typeNodes = 0 m.nimTypes = 0 nullify m.extensionLoaders @@ -1174,7 +1179,7 @@ proc myOpen(module: PSym): PPassContext = let f = if headerFile.len > 0: headerFile else: gProjectFull generatedHeader = rawNewModule(module, changeFileExt(completeCFilePath(f), hExt)) - generatedHeader.isHeaderFile = true + incl generatedHeader.flags, isHeaderFile proc writeHeader(m: BModule) = var result = getCopyright(m.filename) @@ -1306,7 +1311,7 @@ proc myClose(b: PPassContext, n: PNode): PNode = registerModuleToMain(m.module) if sfMainModule in m.module.flags: - m.objHasKidsValid = true + incl m.flags, objHasKidsValid var disp = generateMethodDispatchers() for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym) genMainProc(m) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 187186373..a94950029 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -15,7 +15,7 @@ import from msgs import TLineInfo type - TLabel* = Rope # for the C generator a label is just a rope + TLabel* = Rope # for the C generator a label is just a rope TCFileSection* = enum # the sections a generated C file consists of cfsMergeInfo, # section containing merge information cfsHeaders, # section for C include file headers @@ -89,28 +89,32 @@ type # requires 'T x = T()' to become 'T x; x = T()' # (yes, C++ is weird like that) gcFrameId*: Natural # for the GC stack marking - gcFrameType*: Rope # the struct {} we put the GC markers into + gcFrameType*: Rope # the struct {} we put the GC markers into TTypeSeq* = seq[PType] + + Codegenflag* = enum + preventStackTrace, # true if stack traces need to be prevented + usesThreadVars, # true if the module uses a thread var + frameDeclared, # hack for ROD support so that we don't declare + # a frame var twice in an init proc + isHeaderFile, # C source file is the header file + includesStringh, # C source file already includes ``<string.h>`` + objHasKidsValid # whether we can rely on tfObjHasKids TCGen = object of TPassContext # represents a C source file + s*: TCFileSections # sections of the C file + flags*: set[Codegenflag] module*: PSym filename*: string - s*: TCFileSections # sections of the C file - preventStackTrace*: bool # true if stack traces need to be prevented - usesThreadVars*: bool # true if the module uses a thread var - frameDeclared*: bool # hack for ROD support so that we don't declare - # a frame var twice in an init proc - isHeaderFile*: bool # C source file is the header file - includesStringh*: bool # C source file already includes ``<string.h>`` - objHasKidsValid*: bool # whether we can rely on tfObjHasKids cfilename*: string # filename of the module (including path, # without extension) + tmpBase*: Rope # base for temp identifier generation typeCache*: TIdTable # cache the generated types forwTypeCache*: TIdTable # cache for forward declarations of types - declaredThings*: IntSet # things we have declared in this .c file - declaredProtos*: IntSet # prototypes we have declared in this .c file + declaredThings*: IntSet # things we have declared in this .c file + declaredProtos*: IntSet # prototypes we have declared in this .c file headerFiles*: TLinkedList # needed headers to include - typeInfoMarker*: IntSet # needed for generating type information + typeInfoMarker*: IntSet # needed for generating type information initProc*: BProc # code for init procedure postInitProc*: BProc # code to be executed after the init proc preInitProc*: BProc # code executed before the init proc diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 312afec1a..624c5b183 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -63,19 +63,25 @@ proc sameMethodBucket(a, b: PSym): MethodResult = while true: aa = skipTypes(aa, {tyGenericInst}) bb = skipTypes(bb, {tyGenericInst}) - if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}): + if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef}: aa = aa.lastSon bb = bb.lastSon else: break if sameType(aa, bb): - if aa.kind == tyObject and result != Invalid: result = Yes + if aa.kind == tyObject and result != Invalid: + result = Yes elif aa.kind == tyObject and bb.kind == tyObject: let diff = inheritanceDiff(bb, aa) if diff < 0: - if result != Invalid: result = Yes + if result != Invalid: + result = Yes + else: + return No elif diff != high(int): result = Invalid + else: + return No else: return No @@ -181,7 +187,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = var aa = skipTypes(a.typ.sons[col], skipPtrs) var bb = skipTypes(b.typ.sons[col], skipPtrs) var d = inheritanceDiff(aa, bb) - if (d != high(int)): + if (d != high(int)) and d != 0: return d proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = diff --git a/compiler/commands.nim b/compiler/commands.nim index 86bc1c205..de1197292 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -12,7 +12,7 @@ # We do this here before the 'import' statement so 'defined' does not get # confused with 'TGCMode.gcGenerational' etc. -template bootSwitch(name, expr, userString: expr): expr = +template bootSwitch(name, expr, userString) = # Helper to build boot constants, for debugging you can 'echo' the else part. const name = if expr: " " & userString else: "" @@ -158,7 +158,7 @@ var enableNotes: TNoteKinds disableNotes: TNoteKinds -proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, +proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, info: TLineInfo; orig: string) = var id = "" # arg = "X]:on|off" var i = 0 @@ -181,10 +181,13 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, case whichKeyword(substr(arg, i)) of wOn: incl(gNotes, n) + incl(gMainPackageNotes, n) incl(enableNotes, n) of wOff: excl(gNotes, n) + excl(gMainPackageNotes, n) incl(disableNotes, n) + excl(ForeignPackageNotes, n) else: localError(info, errOnOrOffExpectedButXFound, arg) proc processCompile(filename: string) = @@ -213,6 +216,16 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = of "size": result = contains(gOptions, optOptimizeSize) of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {} else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg) + of "verbosity": result = $gVerbosity == arg + of "app": + case arg.normalize + of "gui": result = contains(gGlobalOptions, optGenGuiApp) + of "console": result = not contains(gGlobalOptions, optGenGuiApp) + of "lib": result = contains(gGlobalOptions, optGenDynLib) and + not contains(gGlobalOptions, optGenGuiApp) + of "staticlib": result = contains(gGlobalOptions, optGenStaticLib) and + not contains(gGlobalOptions, optGenGuiApp) + else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg) else: invalidCmdLineOption(passCmd1, switch, info) proc testCompileOption*(switch: string, info: TLineInfo): bool = @@ -253,20 +266,18 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "experimental": result = gExperimentalMode else: invalidCmdLineOption(passCmd1, switch, info) -proc processPath(path: string, notRelativeToProj = false, - cfginfo = unknownLineInfo()): string = +proc processPath(path: string, info: TLineInfo, + notRelativeToProj = false): string = let p = if notRelativeToProj or os.isAbsolute(path) or '$' in path or path[0] == '.': path else: options.gProjectPath / path - result = unixToNativePath(p % ["nimrod", getPrefixDir(), - "nim", getPrefixDir(), - "lib", libpath, - "home", removeTrailingDirSep(os.getHomeDir()), - "config", cfginfo.toFullPath().splitFile().dir, - "projectname", options.gProjectName, - "projectpath", options.gProjectPath]) + try: + result = pathSubs(p, info.toFullPath().splitFile().dir) + except ValueError: + localError(info, "invalid path: " & p) + result = p proc trackDirty(arg: string, info: TLineInfo) = var a = arg.split(',') @@ -307,19 +318,22 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = case switch.normalize of "path", "p": expectArg(switch, arg, pass, info) - addPath(processPath(arg, cfginfo=info), info) + addPath(processPath(arg, info), info) of "nimblepath", "babelpath": # keep the old name for compat if pass in {passCmd2, passPP} and not options.gNoNimblePath: expectArg(switch, arg, pass, info) - let path = processPath(arg, notRelativeToProj=true) + let path = processPath(arg, info, notRelativeToProj=true) nimblePath(path, info) of "nonimblepath", "nobabelpath": expectNoArg(switch, arg, pass, info) options.gNoNimblePath = true + options.lazyPaths.head = nil + options.lazyPaths.tail = nil + options.lazyPaths.counter = 0 of "excludepath": expectArg(switch, arg, pass, info) - let path = processPath(arg) + let path = processPath(arg, info) lists.excludePath(options.searchPaths, path) lists.excludePath(options.lazyPaths, path) if (len(path) > 0) and (path[len(path) - 1] == DirSep): @@ -328,7 +342,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = lists.excludePath(options.lazyPaths, strippedPath) of "nimcache": expectArg(switch, arg, pass, info) - options.nimcacheDir = processPath(arg, true) + options.nimcacheDir = processPath(arg, info, true) of "out", "o": expectArg(switch, arg, pass, info) options.outFile = arg @@ -339,7 +353,11 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = discard "allow for backwards compatibility, but don't do anything" of "define", "d": expectArg(switch, arg, pass, info) - defineSymbol(arg) + if {':', '='} in arg: + splitSwitch(arg, key, val, pass, info) + defineSymbol(key, val) + else: + defineSymbol(arg) of "undef", "u": expectArg(switch, arg, pass, info) undefSymbol(arg) @@ -407,6 +425,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints() of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) + of "excessivestacktrace": processOnOffSwitchG({optExcessiveStackTrace}, arg, pass, info) of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) of "debugger": case arg.normalize @@ -493,13 +512,13 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg) of "cincludes": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cIncludes.add arg.processPath + if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info) of "clibdir": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLibs.add arg.processPath + if pass in {passCmd2, passPP}: cLibs.add arg.processPath(info) of "clib": expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath + if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info) of "header": headerFile = arg incl(gGlobalOptions, optGenIndex) @@ -540,6 +559,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = gNotes = NotesVerbosity[gVerbosity] incl(gNotes, enableNotes) excl(gNotes, disableNotes) + gMainPackageNotes = gNotes of "parallelbuild": expectArg(switch, arg, pass, info) gNumberOfProcessors = parseInt(arg) @@ -572,7 +592,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info) of "lib": expectArg(switch, arg, pass, info) - libpath = processPath(arg, notRelativeToProj=true) + libpath = processPath(arg, info, notRelativeToProj=true) of "putenv": expectArg(switch, arg, pass, info) splitSwitch(arg, key, val, pass, info) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 60e8f2826..bcd9592e6 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -19,8 +19,8 @@ var gSymbols: StringTableRef const catNone = "false" -proc defineSymbol*(symbol: string) = - gSymbols[symbol] = "true" +proc defineSymbol*(symbol: string, value: string = "true") = + gSymbols[symbol] = value proc undefSymbol*(symbol: string) = gSymbols[symbol] = catNone @@ -62,6 +62,11 @@ proc isDefined*(symbol: string): bool = proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s) +proc lookupSymbol*(symbol: string): string = + result = if isDefined(symbol): gSymbols[symbol] else: nil + +proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s) + iterator definedSymbolNames*: string = for key, val in pairs(gSymbols): if val != catNone: yield key @@ -93,3 +98,4 @@ proc initDefines*() = defineSymbol("nimtypedescfixed") defineSymbol("nimKnowsNimvm") defineSymbol("nimArrIdx") + defineSymbol("nimImmediateDeprecated") diff --git a/compiler/debuginfo.nim b/compiler/debuginfo.nim new file mode 100644 index 000000000..8589730b9 --- /dev/null +++ b/compiler/debuginfo.nim @@ -0,0 +1,81 @@ +# +# +# The Nim Compiler +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## The compiler can generate debuginfo to help debuggers in translating back from C/C++/JS code +## to Nim. The data structure has been designed to produce something useful with Nim's marshal +## module. + +type + FilenameHash* = uint32 + FilenameMapping* = object + package*, file*: string + mangled*: FilenameHash + EnumDesc* = object + size*: int + owner*: FilenameHash + id*: int + name*: string + values*: seq[(string, int)] + DebugInfo* = object + version*: int + files*: seq[FilenameMapping] + enums*: seq[EnumDesc] + conflicts*: bool + +proc sdbmHash(hash: FilenameHash, c: char): FilenameHash {.inline.} = + return FilenameHash(c) + (hash shl 6) + (hash shl 16) - hash + +proc sdbmHash(package, file: string): FilenameHash = + template `&=`(x, c) = x = sdbmHash(x, c) + result = 0 + for i in 0..<package.len: + result &= package[i] + result &= '.' + for i in 0..<file.len: + result &= file[i] + +proc register*(self: var DebugInfo; package, file: string): FilenameHash = + result = sdbmHash(package, file) + for f in self.files: + if f.mangled == result: + if f.package == package and f.file == file: return + self.conflicts = true + break + self.files.add(FilenameMapping(package: package, file: file, mangled: result)) + +proc hasEnum*(self: DebugInfo; ename: string; id: int; owner: FilenameHash): bool = + for en in self.enums: + if en.owner == owner and en.name == ename and en.id == id: return true + +proc registerEnum*(self: var DebugInfo; ed: EnumDesc) = + self.enums.add ed + +proc init*(self: var DebugInfo) = + self.version = 1 + self.files = @[] + self.enums = @[] + +var gDebugInfo*: DebugInfo +debuginfo.init gDebugInfo + +import marshal, streams + +proc writeDebugInfo*(self: var DebugInfo; file: string) = + let s = newFileStream(file, fmWrite) + store(s, self) + s.close + +proc writeDebugInfo*(file: string) = writeDebugInfo(gDebugInfo, file) + +proc loadDebugInfo*(self: var DebugInfo; file: string) = + let s = newFileStream(file, fmRead) + load(s, self) + s.close + +proc loadDebugInfo*(file: string) = loadDebugInfo(gDebugInfo, file) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 8555ec4f0..bbbec081a 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -14,7 +14,7 @@ import ast, strutils, strtabs, options, msgs, os, ropes, idents, wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, - importer, sempass2, json, xmltree, cgi, typesrenderer + importer, sempass2, json, xmltree, cgi, typesrenderer, astalgo type TSections = array[TSymKind, Rope] @@ -25,9 +25,32 @@ type indexValFilename: string analytics: string # Google Analytics javascript, "" if doesn't exist seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. + jArray: JsonNode + types: TStrTable PDoc* = ref TDocumentor ## Alias to type less. +proc whichType(d: PDoc; n: PNode): PSym = + if n.kind == nkSym: + if d.types.strTableContains(n.sym): + result = n.sym + else: + for i in 0..<safeLen(n): + let x = whichType(d, n[i]) + if x != nil: return x + +proc attachToType(d: PDoc; p: PSym): PSym = + let params = p.ast.sons[paramsPos] + # first check the first parameter, then the return type, + # then the other parameter: + template check(i) = + result = whichType(d, params[i]) + if result != nil: return result + + if params.len > 1: check(1) + if params.len > 0: check(0) + for i in 2..<params.len: check(i) + proc compilerMsgHandler(filename: string, line, col: int, msgKind: rst.MsgKind, arg: string) {.procvar.} = # translate msg kind: @@ -81,6 +104,8 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc = result.seenSymbols = newStringTable(modeCaseInsensitive) result.id = 100 + result.jArray = newJArray() + initStrTable result.types proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) = if gCmd != cmdRst2tex: addf(dest, xml, args) @@ -226,10 +251,27 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string = result = esc(d.target, "`") for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter)) result.add esc(d.target, "`") + of nkOpenSymChoice, nkClosedSymChoice: + result = getName(d, n[0], splitAfter) else: internalError(n.info, "getName()") result = "" +proc getNameIdent(n: PNode): PIdent = + case n.kind + of nkPostfix: result = getNameIdent(n.sons[1]) + of nkPragmaExpr: result = getNameIdent(n.sons[0]) + of nkSym: result = n.sym.name + of nkIdent: result = n.ident + of nkAccQuoted: + var r = "" + for i in 0.. <n.len: r.add(getNameIdent(n[i]).s) + result = getIdent(r) + of nkOpenSymChoice, nkClosedSymChoice: + result = getNameIdent(n[0]) + else: + result = nil + proc getRstName(n: PNode): PRstNode = case n.kind of nkPostfix: result = getRstName(n.sons[1]) @@ -239,6 +281,8 @@ proc getRstName(n: PNode): PRstNode = of nkAccQuoted: result = getRstName(n.sons[0]) for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text) + of nkOpenSymChoice, nkClosedSymChoice: + result = getRstName(n[0]) else: internalError(n.info, "getRstName()") result = nil @@ -311,7 +355,7 @@ proc docstringSummary(rstText: string): string = ## Also, we hope to not break the rst, but maybe we do. If there is any ## trimming done, an ellipsis unicode char is added. const maxDocstringChars = 100 - assert (rstText.len < 2 or (rstText[0] == '#' and rstText[1] == '#')) + assert(rstText.len < 2 or (rstText[0] == '#' and rstText[1] == '#')) result = rstText.substr(2).strip var pos = result.find('\L') if pos > 0: @@ -380,13 +424,27 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = "\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) of tkSpaces, tkInvalid: add(result, literal) + of tkCurlyDotLe: + dispA(result, """<span class="Other pragmabegin">$1</span><div class="pragma">""", + "\\spanOther{$1}", + [rope(esc(d.target, literal))]) + of tkCurlyDotRi: + dispA(result, "</div><span class=\"Other pragmaend\">$1</span>", + "\\spanOther{$1}", + [rope(esc(d.target, literal))]) of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, - tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, + tkBracketDotLe, tkBracketDotRi, tkParDotLe, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkAccent, tkColonColon, tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", [rope(esc(d.target, literal))]) + + if k in routineKinds and nameNode.kind == nkSym: + let att = attachToType(d, nameNode.sym) + if att != nil: + dispA(result, """<span class="attachedType" style="visibility:hidden">$1</span>""", "", + [rope esc(d.target, att.name.s)]) inc(d.id) let plainNameRope = rope(xmltree.escape(plainName.strip)) @@ -402,12 +460,11 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = var seeSrcRope: Rope = nil let docItemSeeSrc = getConfigVar("doc.item.seesrc") if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0: - # XXX toFilename doesn't really work. We need to ensure that this keeps - # returning a relative path. + let path = n.info.toFilename.extractFilename.rope let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl, - ["path", "line"], [n.info.toFilename.rope, rope($n.info.line)]) + ["path", "line"], [path, rope($n.info.line)]) dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc, - ["path", "line", "url"], [n.info.toFilename.rope, + ["path", "line", "url"], [path, rope($n.info.line), urlRope])]) add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), @@ -431,8 +488,10 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = setIndexTerm(d[], symbolOrId, name, linkTitle, xmltree.escape(plainDocstring.docstringSummary)) + if k == skType and nameNode.kind == nkSym: + d.types.strTableAdd nameNode.sym -proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = +proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = if not isVisible(nameNode): return var name = getName(d, nameNode) @@ -441,8 +500,8 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) - result = %{ "name": %name, "type": %($k) } - + result = %{ "name": %name, "type": %($k), "line": %n.info.line, + "col": %n.info.col} if comm != nil and comm != "": result["description"] = %comm if r.buf != nil: @@ -492,46 +551,45 @@ proc generateDoc*(d: PDoc, n: PNode) = of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) else: discard -proc generateJson(d: PDoc, n: PNode, jArray: JsonNode = nil): JsonNode = +proc add(d: PDoc; j: JsonNode) = + if j != nil: d.jArray.add j + +proc generateJson*(d: PDoc, n: PNode) = case n.kind of nkCommentStmt: if n.comment != nil and startsWith(n.comment, "##"): let stripped = n.comment.substr(2).strip - result = %{ "comment": %stripped } + d.add %{ "comment": %stripped, "line": %n.info.line, + "col": %n.info.col } of nkProcDef: when useEffectSystem: documentRaises(n) - result = genJSONItem(d, n, n.sons[namePos], skProc) + d.add genJsonItem(d, n, n.sons[namePos], skProc) of nkMethodDef: when useEffectSystem: documentRaises(n) - result = genJSONItem(d, n, n.sons[namePos], skMethod) + d.add genJsonItem(d, n, n.sons[namePos], skMethod) of nkIteratorDef: when useEffectSystem: documentRaises(n) - result = genJSONItem(d, n, n.sons[namePos], skIterator) + d.add genJsonItem(d, n, n.sons[namePos], skIterator) of nkMacroDef: - result = genJSONItem(d, n, n.sons[namePos], skMacro) + d.add genJsonItem(d, n, n.sons[namePos], skMacro) of nkTemplateDef: - result = genJSONItem(d, n, n.sons[namePos], skTemplate) + d.add genJsonItem(d, n, n.sons[namePos], skTemplate) of nkConverterDef: when useEffectSystem: documentRaises(n) - result = genJSONItem(d, n, n.sons[namePos], skConverter) + d.add genJsonItem(d, n, n.sons[namePos], skConverter) of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: for i in countup(0, sonsLen(n) - 1): if n.sons[i].kind != nkCommentStmt: # order is always 'type var let const': - result = genJSONItem(d, n.sons[i], n.sons[i].sons[0], + d.add genJsonItem(d, n.sons[i], n.sons[i].sons[0], succ(skType, ord(n.kind)-ord(nkTypeSection))) of nkStmtList: - result = if jArray != nil: jArray else: newJArray() - for i in countup(0, sonsLen(n) - 1): - var r = generateJson(d, n.sons[i], result) - if r != nil: - result.add(r) - + generateJson(d, n.sons[i]) of nkWhenStmt: # generate documentation for the first branch only: - if not checkForFalse(n.sons[0].sons[0]) and jArray != nil: - discard generateJson(d, lastSon(n.sons[0]), jArray) + if not checkForFalse(n.sons[0].sons[0]): + generateJson(d, lastSon(n.sons[0])) else: discard proc genSection(d: PDoc, kind: TSymKind) = @@ -593,12 +651,36 @@ proc generateIndex*(d: PDoc) = writeIndexFile(d[], splitFile(options.outFile).dir / splitFile(d.filename).name & IndexExt) +proc getOutFile2(filename, ext, dir: string): string = + if gWholeProject: + let d = if options.outFile != "": options.outFile else: dir + createDir(d) + result = d / changeFileExt(filename, ext) + else: + result = getOutFile(filename, ext) + proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = var content = genOutFile(d) if optStdout in gGlobalOptions: writeRope(stdout, content) else: - writeRope(content, getOutFile(filename, outExt), useWarning) + writeRope(content, getOutFile2(filename, outExt, "htmldocs"), useWarning) + +proc writeOutputJson*(d: PDoc, filename, outExt: string, + useWarning = false) = + let content = %*{"orig": d.filename, + "nimble": getPackageName(d.filename), + "entries": d.jArray} + if optStdout in gGlobalOptions: + write(stdout, $content) + else: + var f: File + if open(f, getOutFile2(splitFile(filename).name, + outExt, "jsondocs"), fmWrite): + write(f, $content) + close(f) + else: + discard "fixme: error report" proc commandDoc*() = var ast = parseFile(gProjectMainIdx) @@ -629,18 +711,19 @@ proc commandRst2TeX*() = splitter = "\\-" commandRstAux(gProjectFull, TexExt) -proc commandJSON*() = +proc commandJson*() = var ast = parseFile(gProjectMainIdx) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true - var json = generateJson(d, ast) - var content = rope(pretty(json)) + generateJson(d, ast) + let json = d.jArray + let content = rope(pretty(json)) if optStdout in gGlobalOptions: writeRope(stdout, content) else: - echo getOutFile(gProjectFull, JsonExt) + #echo getOutFile(gProjectFull, JsonExt) writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) proc commandBuildIndex*() = diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 27de06811..d70d5406c 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -19,21 +19,36 @@ type module: PSym PGen = ref TGen -proc close(p: PPassContext, n: PNode): PNode = +template closeImpl(body: untyped) {.dirty.} = var g = PGen(p) let useWarning = sfMainModule notin g.module.flags - if gWholeProject or sfMainModule in g.module.flags: - writeOutput(g.doc, g.module.filename, HtmlExt, useWarning) + #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId + if (g.module.owner.id == gMainPackageId and gWholeProject) or + sfMainModule in g.module.flags: + body try: generateIndex(g.doc) except IOError: discard +proc close(p: PPassContext, n: PNode): PNode = + closeImpl: + writeOutput(g.doc, g.module.filename, HtmlExt, useWarning) + +proc closeJson(p: PPassContext, n: PNode): PNode = + closeImpl: + writeOutputJson(g.doc, g.module.filename, ".json", useWarning) + proc processNode(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) generateDoc(g.doc, n) +proc processNodeJson(c: PPassContext, n: PNode): PNode = + result = n + var g = PGen(c) + generateJson(g.doc, n) + proc myOpen(module: PSym): PPassContext = var g: PGen new(g) @@ -44,6 +59,8 @@ proc myOpen(module: PSym): PPassContext = result = g const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close) +const docgen2JsonPass* = makePass(open = myOpen, process = processNodeJson, + close = closeJson) proc finishDoc2Pass*(project: string) = discard diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index a5a132005..96ede44fd 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -59,7 +59,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = evalTemplateAux(templ.sons[i], actual, c, res) result.add res -proc evalTemplateArgs(n: PNode, s: PSym): PNode = +proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar var totalParams = case n.kind @@ -75,11 +75,11 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode = # their bodies. We could try to fix this, but it may be # wiser to just deprecate immediate templates and macros # now that we have working untyped parameters. - genericParams = if sfImmediate in s.flags: 0 + genericParams = if sfImmediate in s.flags or fromHlo: 0 else: s.ast[genericParamsPos].len expectedRegularParams = <s.typ.len givenRegularParams = totalParams - genericParams - + if givenRegularParams < 0: givenRegularParams = 0 if totalParams > expectedRegularParams + genericParams: globalError(n.info, errWrongNumberOfArguments) @@ -104,14 +104,14 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode = var evalTemplateCounter* = 0 # to prevent endless recursion in templates instantiation -proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode = +proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode = inc(evalTemplateCounter) if evalTemplateCounter > 100: globalError(n.info, errTemplateInstantiationTooNested) result = n # replace each param by the corresponding node: - var args = evalTemplateArgs(n, tmpl) + var args = evalTemplateArgs(n, tmpl, fromHlo) var ctx: TemplCtx ctx.owner = tmpl ctx.genSymOwner = genSymOwner diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 16a8b8bd4..2dcfc0226 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -16,6 +16,8 @@ import lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, securehash, streams +from debuginfo import writeDebugInfo + type TSystemCC* = enum ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, @@ -59,7 +61,7 @@ type # When adding new compilers, the cmake sources could be a good reference: # http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform; -template compiler(name: expr, settings: stmt): stmt {.immediate.} = +template compiler(name, settings: untyped): untyped = proc name: TInfoCC {.compileTime.} = settings # GNU C and C++ Compiler @@ -736,6 +738,8 @@ proc callCCompiler*(projectfile: string) = if not noAbsolutePaths(): if not exefile.isAbsolute(): exefile = joinPath(splitFile(projectfile).dir, exefile) + if optCDebug in gGlobalOptions: + writeDebugInfo(exefile.changeFileExt("ndb")) exefile = quoteShell(exefile) let linkOptions = getLinkOptions() & " " & getConfigVar(cCompiler, ".options.linker") @@ -750,7 +754,7 @@ proc callCCompiler*(projectfile: string) = "lib", quoteShell(libpath)]) if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd, - if gVerbosity > 1: hintExecuting else: hintLinking) + if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking) else: linkCmd = "" if optGenScript in gGlobalOptions: diff --git a/compiler/guards.nim b/compiler/guards.nim index 5ad932e48..4e887d3e3 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -728,12 +728,12 @@ proc simpleSlice*(a, b: PNode): BiggestInt = result = -1 -template isMul(x): expr = x.getMagic in someMul -template isDiv(x): expr = x.getMagic in someDiv -template isAdd(x): expr = x.getMagic in someAdd -template isSub(x): expr = x.getMagic in someSub -template isVal(x): expr = x.kind in {nkCharLit..nkUInt64Lit} -template isIntVal(x, y): expr = x.intVal == y +template isMul(x): untyped = x.getMagic in someMul +template isDiv(x): untyped = x.getMagic in someDiv +template isAdd(x): untyped = x.getMagic in someAdd +template isSub(x): untyped = x.getMagic in someSub +template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit} +template isIntVal(x, y): untyped = x.intVal == y import macros @@ -776,8 +776,8 @@ proc isMinusOne(n: PNode): bool = proc pleViaModel(model: TModel; aa, bb: PNode): TImplication proc ple(m: TModel; a, b: PNode): TImplication = - template `<=?`(a,b): expr = ple(m,a,b) == impYes - template `>=?`(a,b): expr = ple(m, nkIntLit.newIntNode(b), a) == impYes + template `<=?`(a,b): untyped = ple(m,a,b) == impYes + template `>=?`(a,b): untyped = ple(m, nkIntLit.newIntNode(b), a) == impYes # 0 <= 3 if a.isValue and b.isValue: diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 6cc9567af..de0fa6216 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -24,7 +24,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode = of skMacro: result = semMacroExpr(c, n, orig, s) of skTemplate: - result = semTemplateExpr(c, n, s) + result = semTemplateExpr(c, n, s, {efFromHlo}) else: result = semDirectOp(c, n, {}) if optHints in gOptions and hintPattern in gNotes: diff --git a/compiler/idgen.nim b/compiler/idgen.nim index 906c16546..333772705 100644 --- a/compiler/idgen.nim +++ b/compiler/idgen.nim @@ -11,7 +11,7 @@ import idents, strutils, os, options -var gFrontEndId, gBackendId*: int +var gFrontEndId*: int const debugIds* = false @@ -30,10 +30,6 @@ proc getID*(): int {.inline.} = result = gFrontEndId inc(gFrontEndId) -proc backendId*(): int {.inline.} = - result = gBackendId - inc(gBackendId) - proc setId*(id: int) {.inline.} = gFrontEndId = max(gFrontEndId, id + 1) @@ -49,7 +45,6 @@ proc toGid(f: string): string = proc saveMaxIds*(project: string) = var f = open(project.toGid, fmWrite) f.writeLine($gFrontEndId) - f.writeLine($gBackendId) f.close() proc loadMaxIds*(project: string) = @@ -61,5 +56,4 @@ proc loadMaxIds*(project: string) = if f.readLine(line): var backEndId = parseInt(line) gFrontEndId = max(gFrontEndId, frontEndId) - gBackendId = max(gBackendId, backEndId) f.close() diff --git a/compiler/importer.nim b/compiler/importer.nim index 86993358b..dd2c4d954 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -22,7 +22,11 @@ proc getModuleName*(n: PNode): string = # The proc won't perform any checks that the path is actually valid case n.kind of nkStrLit, nkRStrLit, nkTripleStrLit: - result = unixToNativePath(n.strVal) + try: + result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir) + except ValueError: + localError(n.info, "invalid path: " & n.strVal) + result = n.strVal of nkIdent: result = n.ident.s of nkSym: @@ -172,7 +176,7 @@ proc evalImport(c: PContext, n: PNode): PNode = var m = myImportModule(c, n.sons[i]) if m != nil: # ``addDecl`` needs to be done before ``importAllSymbols``! - addDecl(c, m) # add symbol to symbol table of module + addDecl(c, m, n.info) # add symbol to symbol table of module importAllSymbolsExcept(c, m, emptySet) #importForwarded(c, m.ast, emptySet) @@ -182,7 +186,7 @@ proc evalFrom(c: PContext, n: PNode): PNode = var m = myImportModule(c, n.sons[0]) if m != nil: n.sons[0] = newSymNode(m) - addDecl(c, m) # add symbol to symbol table of module + addDecl(c, m, n.info) # add symbol to symbol table of module for i in countup(1, sonsLen(n) - 1): if n.sons[i].kind != nkNilLit: importSymbol(c, n.sons[i], m) @@ -193,7 +197,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode = var m = myImportModule(c, n.sons[0]) if m != nil: n.sons[0] = newSymNode(m) - addDecl(c, m) # add symbol to symbol table of module + addDecl(c, m, n.info) # add symbol to symbol table of module var exceptSet = initIntSet() for i in countup(1, sonsLen(n) - 1): let ident = lookups.considerQuotedIdent(n.sons[i]) diff --git a/compiler/installer.ini b/compiler/installer.ini index 12d9baf82..b802f08f1 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -8,7 +8,7 @@ Platforms: """ windows: i386;amd64 linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64 macosx: i386;amd64;powerpc64 - solaris: i386;amd64;sparc + solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64 netbsd: i386;amd64 openbsd: i386;amd64 @@ -48,8 +48,8 @@ Start: "doc/overview.html" [Other] Files: "readme.txt;install.txt;contributors.txt;copying.txt" Files: "makefile" -Files: "*.ini" Files: "koch.nim" +Files: "install_nimble.nims" Files: "icons/nim.ico" Files: "icons/nim.rc" @@ -60,176 +60,26 @@ Files: "icons/koch.rc" Files: "icons/koch.res" Files: "icons/koch_icon.o" -Files: "compiler/readme.txt" -Files: "compiler/installer.ini" -Files: "compiler/*.cfg" -Files: "compiler/*.nim" -Files: "doc/*.txt" -Files: "doc/manual/*.txt" -Files: "doc/*.nim" -Files: "doc/*.cfg" -Files: "compiler/nimfix/*.nim" -Files: "compiler/nimfix/*.cfg" -Files: "compiler/nimsuggest/*.nim" -Files: "compiler/nimsuggest/*.cfg" -Files: "compiler/plugins/locals/*.nim" -Files: "compiler/plugins/*.nim" -Files: "tools/*.nim" -Files: "tools/*.cfg" -Files: "tools/*.tmpl" -Files: "tools/niminst/*.nim" -Files: "tools/niminst/*.cfg" -Files: "tools/niminst/*.tmpl" -Files: "tools/niminst/*.nsh" +Files: "compiler" +Files: "doc" +Files: "tools" Files: "web/website.ini" +Files: "web/ticker.html" Files: "web/*.nim" -Files: "web/*.txt" +Files: "web/*.rst" +Files: "web/*.csv" +Files: "web/news/*.rst" Files: "bin/nimblepkg/*.nim" Files: "bin/nimblepkg/*.cfg" [Lib] -Files: "lib/nimbase.h" -Files: "lib/*.nim" -Files: "lib/*.cfg" -Files: "lib/*.nimble" - -Files: "lib/system/*.nim" -Files: "lib/core/*.nim" -Files: "lib/pure/*.nim" -Files: "lib/pure/*.cfg" -Files: "lib/pure/collections/*.nim" -Files: "lib/pure/concurrency/*.nim" -Files: "lib/pure/unidecode/*.nim" -Files: "lib/pure/concurrency/*.cfg" -Files: "lib/impure/*.nim" -Files: "lib/impure/nre/private/*.nim" -Files: "lib/wrappers/*.nim" -Files: "lib/arch/*.nim" - -Files: "lib/wrappers/readline/*.nim" -Files: "lib/wrappers/linenoise/*.nim" -Files: "lib/wrappers/linenoise/*.c" -Files: "lib/wrappers/linenoise/*.h" - -Files: "lib/windows/*.nim" -Files: "lib/posix/*.nim" -Files: "lib/js/*.nim" -Files: "lib/packages/docutils/*.nim" - -Files: "lib/deprecated/core/*.nim" -Files: "lib/deprecated/pure/*.nim" -Files: "lib/deprecated/pure/*.cfg" +Files: "lib" [Other] -Files: "examples/*.nim" -Files: "examples/c++iface/*.nim" -Files: "examples/objciface/*.nim" -Files: "examples/cross_calculator/" - -Files: "examples/*.html" -Files: "examples/*.txt" -Files: "examples/*.cfg" -Files: "examples/*.tmpl" +Files: "examples" +#Files: "dist/nimble" -Files: "tests/actiontable/*.nim" -Files: "tests/alias/*.nim" -Files: "tests/ambsym/*.nim" -Files: "tests/array/*.nim" -Files: "tests/assign/*.nim" -Files: "tests/astoverload/*.nim" -Files: "tests/async/*.nim" -Files: "tests/benchmarks/*.nim" -Files: "tests/bind/*.nim" -Files: "tests/borrow/*.nim" -Files: "tests/casestmt/*.nim" -Files: "tests/ccgbugs/*.nim" -Files: "tests/clearmsg/*.nim" -Files: "tests/closure/*.nim" -Files: "tests/cnstseq/*.nim" -Files: "tests/collections/*.nim" -Files: "tests/compiles/*.nim" -Files: "tests/concat/*.nim" -Files: "tests/concepts/*.nim" -Files: "tests/constr/*.nim" -Files: "tests/constraints/*.nim" -Files: "tests/controlflow/*.nim" -Files: "tests/converter/*.nim" -Files: "tests/cpp/*.nim" -Files: "tests/defaultprocparam/*.nim" -Files: "tests/deprecated/*.nim" -Files: "tests/destructor/*.nim" -Files: "tests/dir with space/*.nim" -Files: "tests/discard/*.nim" -Files: "tests/distinct/*.nim" -Files: "tests/dll/*.nim" -Files: "tests/effects/*.nim" -Files: "tests/enum/*.nim" -Files: "tests/exception/*.nim" -Files: "tests/exprs/*.nim" -Files: "tests/fields/*.nim" -Files: "tests/float/*.nim" -Files: "tests/friends/*.nim" -Files: "tests/gc/*.nim" -Files: "tests/generics/*.nim" -Files: "tests/gensym/*.nim" -Files: "tests/global/*.nim" -Files: "tests/implicit/*.nim" -Files: "tests/init/*.nim" -Files: "tests/iter/*.nim" -Files: "tests/js/*.nim" -Files: "tests/js/*.cfg" -Files: "tests/let/*.nim" -Files: "tests/lexer/*.nim" -Files: "tests/lookups/*.nim" -Files: "tests/macros/*.nim" -Files: "tests/magics/*.nim" -Files: "tests/metatype/*.nim" -Files: "tests/method/*.nim" -Files: "tests/misc/*.nim" -Files: "tests/modules/*.nim" -Files: "tests/namedparams/*.nim" -Files: "tests/notnil/*.nim" -Files: "tests/objects/*.nim" -Files: "tests/objvariant/*.nim" -Files: "tests/openarray/*.nim" -Files: "tests/osproc/*.nim" -Files: "tests/overflw/*.nim" -Files: "tests/overload/*.nim" -Files: "tests/parallel/*.nim" -Files: "tests/parallel/*.cfg" -Files: "tests/parser/*.nim" -Files: "tests/pragmas/*.nim" -Files: "tests/proc/*.nim" -Files: "tests/procvar/*.nim" -Files: "tests/range/*.nim" -Files: "tests/rodfiles/*.nim" -Files: "tests/seq/*.nim" -Files: "tests/sets/*.nim" -Files: "tests/showoff/*.nim" -Files: "tests/specialops/*.nim" -Files: "tests/stdlib/*.nim" -Files: "tests/system/*.nim" -Files: "tests/template/*.nim" -Files: "tests/testament/*.nim" -Files: "tests/testdata/*.csv" -Files: "tests/testdata/*.html" -Files: "tests/testdata/*.json" -Files: "tests/testdata/*.txt" -Files: "tests/testdata/*.xml" -Files: "tests/threads/*.nim" -Files: "tests/threads/*.cfg" -Files: "tests/trmacros/*.nim" -Files: "tests/tuples/*.nim" -Files: "tests/typerel/*.nim" -Files: "tests/types/*.nim" -Files: "tests/usingstmt/*.nim" -Files: "tests/varres/*.nim" -Files: "tests/varstmt/*.nim" -Files: "tests/vm/*.nim" -Files: "tests/readme.txt" -Files: "tests/testament/css/*.css" -Files: "tests/testament/*.cfg" -Files: "lib/pure/unidecode/unidecode.dat" +Files: "tests" [Windows] Files: "bin/nim.exe" @@ -249,7 +99,7 @@ BinPath: r"bin;dist\mingw\bin;dist" Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html" Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip" Download: r"Support DLL's|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip" -Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe" +Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe" ; for now only NSIS supports optional downloads [UnixBin] @@ -279,3 +129,7 @@ buildDepends: "gcc (>= 4:4.3.2)" pkgDepends: "gcc (>= 4:4.3.2)" shortDesc: "The Nim Compiler" licenses: "bin/nim,MIT;lib/*,MIT;" + +[nimble] +pkgName: "compiler" +pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt" diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 2da7db900..80bcd2b0e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -8,7 +8,7 @@ # # This is the JavaScript code generator. -# Soon also a PHP code generator. ;-) +# Also a PHP code generator. ;-) discard """ The JS code generator contains only 2 tricks: @@ -48,6 +48,7 @@ type etyNull, # null type etyProc, # proc type etyBool, # bool type + etySeq, # Nim seq or string type etyInt, # JavaScript's int etyFloat, # JavaScript's float etyString, # JavaScript's string @@ -71,7 +72,7 @@ type isLoop: bool # whether it's a 'block' or 'while' TGlobals = object - typeInfo, code: Rope + typeInfo, constants, code: Rope forwarded: seq[PSym] generatedSyms: IntSet typeInfoGenerated: IntSet @@ -94,7 +95,7 @@ type up: PProc # up the call chain; required for closure support declaredGlobals: IntSet -template `|`(a, b: expr): expr {.immediate, dirty.} = +template `|`(a, b: untyped): untyped {.dirty.} = (if p.target == targetJS: a else: b) proc newGlobals(): PGlobals = @@ -156,15 +157,18 @@ proc mapType(typ: PType): TJSTypeKind = of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat of tySet: result = etyObject # map a set to a table - of tyString, tySequence: result = etyInt # little hack to get right semantics + of tyString, tySequence: result = etySeq of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum, tyVarargs: result = etyObject of tyNil: result = etyNull of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation, tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor, - tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses: + tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid: result = etyNone + of tyStatic: + if t.n != nil: result = mapType(lastSon t) + else: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString @@ -237,7 +241,7 @@ proc useMagic(p: PProc, name: string) = internalAssert s.kind in {skProc, skMethod, skConverter} if not p.g.generatedSyms.containsOrIncl(s.id): let code = genProc(p, s) - add(p.g.code, code) + add(p.g.constants, code) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so @@ -359,8 +363,8 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr - ["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 != $2)", "($1 != $2)"], # Xor + ["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 == $2)", "($1 == $2)"], # EqProc ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 @@ -817,7 +821,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc]) return - let xtyp = mapType(p, x.typ) + var xtyp = mapType(p, x.typ) if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: gen(p, x.sons[0], a) @@ -829,7 +833,18 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = gen(p, y, b) + # we don't care if it's an etyBaseIndex (global) of a string, it's + # still a string that needs to be copied properly: + if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}: + xtyp = etySeq case xtyp + of etySeq: + if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + addf(p.body, "$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)]) of etyObject: if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) @@ -981,7 +996,7 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = r.res = "nimAt($1, $2)" % [r.address, r.res] elif ty.kind in {tyString, tyCString}: # XXX this needs to be more like substr($1,$2) - r.res = "ord($1[$2])" % [r.address, r.res] + r.res = "ord(@$1[$2])" % [r.address, r.res] else: r.res = "$1[$2]" % [r.address, r.res] else: @@ -1049,6 +1064,8 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') of nkObjDownConv: gen(p, n.sons[0], r) + of nkHiddenDeref: + gen(p, n.sons[0].sons[0], r) else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind) proc thisParam(p: PProc; typ: PType): PType = @@ -1215,11 +1232,13 @@ proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType; genArgNoParam(p, it, r) else: genArg(p, it, paramType.sym, r) + inc generated proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; r: var TCompRes) = var i = 0 var j = 1 + r.kind = resExpr while i < pat.len: case pat[i] of '@': @@ -1233,31 +1252,43 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; genOtherArg(p, n, j, typ, generated, r) inc j inc i + of '\31': + # unit separator + add(r.res, "#") + inc i + of '\29': + # group separator + add(r.res, "@") + inc i else: let start = i while i < pat.len: - if pat[i] notin {'@', '#'}: inc(i) + if pat[i] notin {'@', '#', '\31', '\29'}: inc(i) else: break if i - 1 >= start: add(r.res, substr(pat, start, i - 1)) proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = # don't call '$' here for efficiency: - let pat = n.sons[0].sym.loc.r.data - internalAssert pat != nil - if pat.contains({'#', '(', '@'}): - var typ = skipTypes(n.sons[0].typ, abstractInst) - assert(typ.kind == tyProc) - genPatternCall(p, n, pat, typ, r) - return - gen(p, n.sons[1], r) - if r.typ == etyBaseIndex: - if r.address == nil: - globalError(n.info, "cannot invoke with infix syntax") - r.res = "$1[$2]" % [r.address, r.res] - r.address = nil - r.typ = etyNone - add(r.res, "." | "->") + let f = n[0].sym + if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if sfInfixCall in f.flags: + let pat = n.sons[0].sym.loc.r.data + internalAssert pat != nil + if pat.contains({'#', '(', '@'}): + var typ = skipTypes(n.sons[0].typ, abstractInst) + assert(typ.kind == tyProc) + genPatternCall(p, n, pat, typ, r) + return + if n.len != 1: + gen(p, n.sons[1], r) + if r.typ == etyBaseIndex: + if r.address == nil: + globalError(n.info, "cannot invoke with infix syntax") + r.res = "$1[$2]" % [r.address, r.res] + r.address = nil + r.typ = etyNone + add(r.res, "." | "->") var op: TCompRes if p.target == targetPHP: op.kind = resCallee @@ -1266,7 +1297,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = genArgs(p, n, r, 2) proc genCall(p: PProc, n: PNode, r: var TCompRes) = - if thisParam(p, n.sons[0].typ) != nil: + if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil: genInfixCall(p, n, r) return if p.target == targetPHP: @@ -1400,6 +1431,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("null", indirect) of tySequence, tyString, tyCString, tyPointer, tyProc: result = putToSeq("null", indirect) + of tyStatic: + if t.n != nil: + result = createVar(p, lastSon t, indirect) + else: + internalError("createVar: " & $t.kind) + result = nil else: internalError("createVar: " & $t.kind) result = nil @@ -1409,13 +1446,16 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = a: TCompRes s: Rope if n.kind == nkEmpty: + let mname = mangleName(v, p.target) addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", - [mangleName(v, p.target), createVar(p, v.typ, isIndirect(v))]) + [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 ]) else: discard mangleName(v, p.target) gen(p, n, a) case mapType(p, v.typ) - of etyObject: + of etyObject, etySeq: if needsNoCopy(p, n): s = a.res else: @@ -1458,7 +1498,7 @@ proc genConstant(p: PProc, c: PSym) = p.body = nil #genLineDir(p, c.ast) genVarInit(p, c, c.ast) - add(p.g.code, p.body) + add(p.g.constants, p.body) p.body = oldBody proc genNew(p: PProc, n: PNode) = @@ -1554,8 +1594,16 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = of tyEnum, tyOrdinal: gen(p, n.sons[1], r) useMagic(p, "cstrToNimstr") + var offset = "" + if t.kind == tyEnum: + let firstFieldOffset = t.n.sons[0].sym.position + if firstFieldOffset < 0: + offset = "+" & $(-firstFieldOffset) + elif firstFieldOffset > 0: + offset = "-" & $firstFieldOffset + r.kind = resExpr - r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res] + r.res = "cstrToNimstr($1.node.sons[$2$3].name)" % [genTypeInfo(p, t), r.res, rope(offset)] else: # XXX: internalError(n.info, "genRepr: Not implemented") @@ -1645,8 +1693,11 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mOrd: genOrd(p, n, r) of mLengthStr: - unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" | - "strlen($1)") + if p.target == targetJS and n.sons[1].typ.skipTypes(abstractInst).kind == tyCString: + unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") + else: + unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" | + "strlen($1)") of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)") of mLengthSeq, mLengthOpenArray, mLengthArray: unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" | @@ -1672,8 +1723,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") 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" | "") - of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $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 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)") @@ -1683,8 +1735,31 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]") - of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)" | "isset($1[$2])") + of mInSet: + if p.target == targetJS: + binaryExpr(p, n, r, "", "($1[$2] != undefined)") + else: + let s = n.sons[1] + if s.kind == nkCurly: + var a, b, x: TCompRes + gen(p, n.sons[2], x) + r.res = rope("(") + r.kind = resExpr + for i in countup(0, sonsLen(s) - 1): + if i > 0: add(r.res, " || ") + var it = s.sons[i] + if it.kind == nkRange: + gen(p, it.sons[0], a) + gen(p, it.sons[1], b) + addf(r.res, "($1 >= $2 && $1 <= $3)", [x.res, a.res, b.res,]) + else: + gen(p, it, a) + addf(r.res, "($1 == $2)", [x.res, a.res]) + add(r.res, ")") + else: + binaryExpr(p, n, r, "", "isset($1[$2])") of mNewSeq: genNewSeq(p, n) + of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]" | "array()") of mOf: genOf(p, n, r) of mReset: genReset(p, n) of mEcho: genEcho(p, n, r) @@ -1877,9 +1952,13 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = let header = generateHeader(p, prc.typ) 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") % [ - mangleName(resultSym, p.target), + mname, createVar(p, resultSym.typ, isIndirect(resultSym))] + if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex: + resultAsgn.add "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] @@ -1983,7 +2062,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): genMagic(p, n, r) elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and - n.len >= 2: + n.len >= 1: genInfixCall(p, n, r) else: genCall(p, n, r) @@ -2131,7 +2210,7 @@ proc wholeCode*(m: BModule): Rope = var p = newProc(globals, m, nil, m.module.options) attachProc(p, prc) - result = globals.typeInfo & globals.code + result = globals.typeInfo & globals.constants & globals.code proc getClassName(t: PType): Rope = var s = t.sym diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 8d109e48a..cf1679ee4 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -165,4 +165,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = of tyEnum: genEnumInfo(p, t, result) of tyObject: genObjectInfo(p, t, result) of tyTuple: genTupleInfo(p, t, result) + of tyStatic: + if t.n != nil: result = genTypeInfo(p, lastSon t) + else: internalError("genTypeInfo(" & $t.kind & ')') else: internalError("genTypeInfo(" & $t.kind & ')') diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 53fca4863..753602c80 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -716,11 +716,16 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; #localError(n.info, "internal error: closure to closure created") # now we know better, so patch it: n.sons[0] = x.sons[0] + n.sons[1] = x.sons[1] of nkLambdaKinds, nkIteratorDef: if n.typ != nil and n[namePos].kind == nkSym: let m = newSymNode(n[namePos].sym) m.typ = n.typ result = liftCapturedVars(m, owner, d, c) + of nkHiddenStdConv: + if n.len == 2: + n.sons[1] = liftCapturedVars(n[1], owner, d, c) + if n[1].kind == nkClosure: result = n[1] else: if owner.isIterator: if n.kind == nkYieldStmt: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0032b97df..0eee5004e 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -138,6 +138,8 @@ proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = proc isKeyword*(kind: TTokType): bool = result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) +template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion + proc isNimIdentifier*(s: string): bool = if s[0] in SymStartChars: var i = 1 @@ -589,12 +591,29 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = of '\\': add(tok.literal, '\\') inc(L.bufpos) - of 'x', 'X': + of 'x', 'X', 'u', 'U': + var tp = L.buf[L.bufpos] inc(L.bufpos) var xi = 0 handleHexChar(L, xi) handleHexChar(L, xi) - add(tok.literal, chr(xi)) + if tp in {'u', 'U'}: + handleHexChar(L, xi) + handleHexChar(L, xi) + # inlined toUTF-8 to avoid unicode and strutils dependencies. + if xi <=% 127: + add(tok.literal, xi.char ) + elif xi <=% 0x07FF: + add(tok.literal, ((xi shr 6) or 0b110_00000).char ) + add(tok.literal, ((xi and ones(6)) or 0b10_0000_00).char ) + elif xi <=% 0xFFFF: + add(tok.literal, (xi shr 12 or 0b1110_0000).char ) + add(tok.literal, (xi shr 6 and ones(6) or 0b10_0000_00).char ) + add(tok.literal, (xi and ones(6) or 0b10_0000_00).char ) + else: # value is 0xFFFF + add(tok.literal, "\xef\xbf\xbf" ) + else: + add(tok.literal, chr(xi)) of '0'..'9': if matchTwoChars(L, '0', {'0'..'9'}): lexMessage(L, warnOctalEscape) @@ -716,7 +735,8 @@ proc getSymbol(L: var TLexer, tok: var TToken) = if c == '\226' and buf[pos+1] == '\128' and buf[pos+2] == '\147': # It's a 'magic separator' en-dash Unicode - if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars: + if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars or + isMagicIdentSeparatorRune(buf, pos+magicIdentSeparatorRuneByteWidth) or pos == L.bufpos: lexMessage(L, errInvalidToken, "–") break inc(pos, magicIdentSeparatorRuneByteWidth) @@ -728,7 +748,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) = h = h !& ord(c) inc(pos) of '_': - if buf[pos+1] notin SymChars: + if buf[pos+1] notin SymChars or isMagicIdentSeparatorRune(buf, pos+1): lexMessage(L, errInvalidToken, "_") break inc(pos) @@ -811,10 +831,6 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int; break dec nesting inc pos - of '\t': - lexMessagePos(L, errTabulatorsAreNotAllowed, pos) - inc(pos) - if isDoc: tok.literal.add '\t' of CR, LF: pos = handleCRLF(L, pos) buf = L.buf @@ -877,10 +893,10 @@ proc scanComment(L: var TLexer, tok: var TToken) = inc(indent) when defined(nimfix): - template doContinue(): expr = + template doContinue(): untyped = buf[pos] == '#' and (col == indent or lastBackslash > 0) else: - template doContinue(): expr = + template doContinue(): untyped = buf[pos] == '#' and buf[pos+1] == '#' if doContinue(): tok.literal.add "\n" @@ -926,9 +942,9 @@ proc skip(L: var TLexer, tok: var TToken) = break tok.strongSpaceA = 0 when defined(nimfix): - template doBreak(): expr = buf[pos] > ' ' + template doBreak(): untyped = buf[pos] > ' ' else: - template doBreak(): expr = + template doBreak(): untyped = buf[pos] > ' ' and (buf[pos] != '#' or buf[pos+1] == '#') if doBreak(): tok.indent = indent @@ -1041,7 +1057,8 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = inc(L.bufpos) of '_': inc(L.bufpos) - if L.buf[L.bufpos] notin SymChars: + if L.buf[L.bufpos] notin SymChars+{'_'} and not + isMagicIdentSeparatorRune(L.buf, L.bufpos): tok.tokType = tkSymbol tok.ident = getIdent("_") else: @@ -1062,6 +1079,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = tok.tokType = tkCharLit of '0'..'9': getNumber(L, tok) + let c = L.buf[L.bufpos] + if c in SymChars+{'_'}: + lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') else: if c in OpChars: getOperator(L, tok) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index a337bf0f3..df19a6afb 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -99,8 +99,11 @@ proc debugScopes*(c: PContext; limit=0) {.deprecated.} = proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym = for scope in walkScopes(c.currentScope): - result = strTableGet(scope.symbols, s) - if result != nil and result.kind in filter: return + var ti: TIdentIter + var candidate = initIdentIter(ti, scope.symbols, s) + while candidate != nil: + if candidate.kind in filter: return candidate + candidate = nextIdentIter(ti, scope.symbols) result = nil proc errorSym*(c: PContext, n: PNode): PSym = @@ -153,7 +156,8 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) = if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: # XXX: implicit type params are currently skTypes # maybe they can be made skGenericParam as well. - if s.typ != nil and tfImplicitTypeParam notin s.typ.flags: + if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and + s.typ.kind != tyGenericParam: message(s.info, hintXDeclaredButNotUsed, getSymRepr(s)) s = nextIter(it, scope.symbols) @@ -161,6 +165,10 @@ proc wrongRedefinition*(info: TLineInfo, s: string) = if gCmd != cmdInteractive: localError(info, errAttemptToRedefine, s) +proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) = + if not c.currentScope.addUniqueSym(sym): + wrongRedefinition(info, sym.name.s) + proc addDecl*(c: PContext, sym: PSym) = if not c.currentScope.addUniqueSym(sym): wrongRedefinition(sym.info, sym.name.s) @@ -212,14 +220,27 @@ when defined(nimfix): of 'a'..'z': result = getIdent(toLower(x.s[0]) & x.s.substr(1)) else: result = x - template fixSpelling(n: PNode; ident: PIdent; op: expr) = + template fixSpelling(n: PNode; ident: PIdent; op: untyped) = let alt = ident.altSpelling result = op(c, alt).skipAlias(n) if result != nil: prettybase.replaceDeprecated(n.info, ident, alt) return result else: - template fixSpelling(n: PNode; ident: PIdent; op: expr) = discard + template fixSpelling(n: PNode; ident: PIdent; op: untyped) = discard + +proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = + var err = "Error: ambiguous identifier: '" & s.name.s & "'" + var ti: TIdentIter + var candidate = initIdentIter(ti, c.importTable.symbols, s.name) + var i = 0 + while candidate != nil: + if i == 0: err.add " --use " + else: err.add " or " + err.add candidate.owner.name.s & "." & candidate.name.s + candidate = nextIdentIter(ti, c.importTable.symbols) + inc i + localError(info, errGenerated, err) proc lookUp*(c: PContext, n: PNode): PSym = # Looks up a symbol. Generates an error in case of nil. @@ -243,32 +264,36 @@ proc lookUp*(c: PContext, n: PNode): PSym = internalError(n.info, "lookUp") return if contains(c.ambiguousSymbols, result.id): - localError(n.info, errUseQualifier, result.name.s) + errorUseQualifier(c, n.info, result) if result.kind == skStub: loadStub(result) type TLookupFlag* = enum - checkAmbiguity, checkUndeclared + checkAmbiguity, checkUndeclared, checkModule -proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym = +proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = + const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage} case n.kind of nkIdent, nkAccQuoted: var ident = considerQuotedIdent(n) - result = searchInScopes(c, ident).skipAlias(n) + if checkModule in flags: + result = searchInScopes(c, ident).skipAlias(n) + else: + result = searchInScopes(c, ident, allExceptModule).skipAlias(n) if result == nil and checkUndeclared in flags: fixSpelling(n, ident, searchInScopes) localError(n.info, errUndeclaredIdentifier, ident.s) result = errorSym(c, n) elif checkAmbiguity in flags and result != nil and contains(c.ambiguousSymbols, result.id): - localError(n.info, errUseQualifier, ident.s) + errorUseQualifier(c, n.info, result) of nkSym: result = n.sym if checkAmbiguity in flags and contains(c.ambiguousSymbols, result.id): - localError(n.info, errUseQualifier, n.sym.name.s) + errorUseQualifier(c, n.info, n.sym) of nkDotExpr: result = nil - var m = qualifiedLookUp(c, n.sons[0], flags*{checkUndeclared}) + var m = qualifiedLookUp(c, n.sons[0], (flags*{checkUndeclared})+{checkModule}) if m != nil and m.kind == skModule: var ident: PIdent = nil if n.sons[1].kind == nkIdent: @@ -313,7 +338,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.mode = oimDone of nkDotExpr: o.mode = oimOtherModule - o.m = qualifiedLookUp(c, n.sons[0]) + o.m = qualifiedLookUp(c, n.sons[0], {checkUndeclared, checkModule}) if o.m != nil and o.m.kind == skModule: var ident: PIdent = nil if n.sons[1].kind == nkIdent: diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 7a5c7f44b..36eb24653 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -471,6 +471,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; let slice = newNodeI(nkCall, n.info, 4) slice.typ = n.typ slice.sons[0] = newSymNode(createMagic("slice", mSlice)) + slice.sons[0].typ = getSysType(tyInt) # fake type var fieldB = newSym(skField, tmpName, objType.owner, n.info) fieldB.typ = getSysType(tyInt) objType.addField(fieldB) @@ -577,6 +578,8 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; var fn = n.sons[0] # templates and macros are in fact valid here due to the nature of # the transformation: + if fn.kind == nkClosure: + localError(n.info, "closure in spawn environment is not allowed") if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro, skMethod, skConverter}): # for indirect calls we pass the function pointer in the scratchObj diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 40e0728ae..13b365d04 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -41,11 +41,15 @@ proc getSysSym*(name: string): PSym = proc getSysMagic*(name: string, m: TMagic): PSym = var ti: TIdentIter let id = getIdent(name) - result = initIdentIter(ti, systemModule.tab, id) - while result != nil: - if result.kind == skStub: loadStub(result) - if result.magic == m: return result - result = nextIdentIter(ti, systemModule.tab) + var r = initIdentIter(ti, systemModule.tab, id) + while r != nil: + if r.kind == skStub: loadStub(r) + if r.magic == m: + # prefer the tyInt variant: + if r.typ.sons[0].kind == tyInt: return r + result = r + r = nextIdentIter(ti, systemModule.tab) + if result != nil: return result rawMessage(errSystemNeeds, name) result = newSym(skError, id, systemModule, systemModule.info) result.typ = newType(tyError, systemModule) diff --git a/compiler/main.nim b/compiler/main.nim index ece21a817..0db66b53e 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -41,14 +41,16 @@ proc commandGenDepend = proc commandCheck = msgs.gErrorMax = high(int) # do not stop after first error + defineSymbol("nimcheck") semanticPasses() # use an empty backend for semantic checking only rodPass() compileProject() -proc commandDoc2 = +proc commandDoc2(json: bool) = msgs.gErrorMax = high(int) # do not stop after first error semanticPasses() - registerPass(docgen2Pass) + if json: registerPass(docgen2JsonPass) + else: registerPass(docgen2Pass) #registerPass(cleanupPass()) compileProject() finishDoc2Pass(gProjectName) @@ -241,7 +243,7 @@ proc mainCommand* = clearPasses() gLastCmdTime = epochTime() appendStr(searchPaths, options.libpath) - if gProjectFull.len != 0: + when false: # gProjectFull.len != 0: # current path is always looked first for modules prependStr(searchPaths, gProjectPath) setId(100) @@ -280,7 +282,7 @@ proc mainCommand* = gCmd = cmdDoc loadConfigs(DocConfig) defineSymbol("nimdoc") - commandDoc2() + commandDoc2(false) of "rst2html": gCmd = cmdRst2html loadConfigs(DocConfig) @@ -295,7 +297,13 @@ proc mainCommand* = loadConfigs(DocConfig) wantMainModule() defineSymbol("nimdoc") - commandJSON() + commandJson() + of "jsondoc2": + gCmd = cmdDoc + loadConfigs(DocConfig) + wantMainModule() + defineSymbol("nimdoc") + commandDoc2(true) of "buildindex": gCmd = cmdDoc loadConfigs(DocConfig) diff --git a/compiler/modules.nim b/compiler/modules.nim index ef727e200..9120bd1b6 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -29,12 +29,16 @@ var gMemCacheData*: seq[TModuleInMemory] = @[] ## XXX: we should implement recycling of file IDs ## if the user keeps renaming modules, the file IDs will keep growing + gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why. + packageSyms: TStrTable + +initStrTable(packageSyms) proc getModule*(fileIdx: int32): PSym = if fileIdx >= 0 and fileIdx < gCompiledModules.len: result = gCompiledModules[fileIdx] -template hash(x: PSym): expr = +template hash(x: PSym): untyped = gMemCacheData[x.position].hash proc hashChanged(fileIdx: int32): bool = @@ -45,7 +49,7 @@ proc hashChanged(fileIdx: int32): bool = else: hashNotChanged # echo "TESTING Hash: ", fileIdx.toFilename, " ", result - case gMemCacheData[fileIdx].hashStatus: + case gMemCacheData[fileIdx].hashStatus of hashHasChanged: result = true of hashNotChanged: @@ -90,6 +94,7 @@ proc resetAllModules* = if gCompiledModules[i] != nil: resetModule(i.int32) resetPackageCache() + initStrTable(packageSyms) # for m in cgenModules(): echo "CGEN MODULE FOUND" proc resetAllModulesHard* = @@ -97,6 +102,7 @@ proc resetAllModulesHard* = gCompiledModules.setLen 0 gMemCacheData.setLen 0 magicsys.resetSysTypes() + initStrTable(packageSyms) # XXX #gOwners = @[] @@ -105,12 +111,16 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile = resetModule(fileIdx) return Yes - if gMemCacheData[fileIdx].needsRecompile != Maybe: - return gMemCacheData[fileIdx].needsRecompile + if gFuzzyGraphChecking: + if gMemCacheData[fileIdx].needsRecompile != Maybe: + return gMemCacheData[fileIdx].needsRecompile + else: + # cycle detection: We claim that a cycle does no harm. + if gMemCacheData[fileIdx].needsRecompile == Probing: + return No - if optForceFullMake in gGlobalOptions or - hashChanged(fileIdx): - markDirty + if optForceFullMake in gGlobalOptions or hashChanged(fileIdx): + markDirty() if gMemCacheData[fileIdx].deps != nil: gMemCacheData[fileIdx].needsRecompile = Probing @@ -118,7 +128,7 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile = let d = checkDepMem(dep) if d in {Yes, Recompiled}: # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d - markDirty + markDirty() gMemCacheData[fileIdx].needsRecompile = No return No @@ -135,8 +145,16 @@ proc newModule(fileIdx: int32): PSym = rawMessage(errInvalidModuleName, result.name.s) result.info = newLineInfo(fileIdx, 1, 1) - result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil, - result.info) + let pack = getIdent(getPackageName(filename)) + var packSym = packageSyms.strTableGet(pack) + if packSym == nil: + let pck = getPackageName(filename) + let pck2 = if pck.len > 0: pck else: "unknown" + packSym = newSym(skPackage, getIdent(pck2), nil, result.info) + initStrTable(packSym.tab) + packageSyms.strTableAdd(packSym) + + result.owner = packSym result.position = fileIdx growCache gMemCacheData, fileIdx @@ -146,6 +164,11 @@ proc newModule(fileIdx: int32): PSym = incl(result.flags, sfUsed) initStrTable(result.tab) strTableAdd(result.tab, result) # a module knows itself + let existing = strTableGet(packSym.tab, result.name) + if existing != nil and existing.info.fileIndex != result.info.fileIndex: + localError(result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath) + # strTableIncl() for error corrections: + discard strTableIncl(packSym.tab, result) proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym = result = getModule(fileIdx) @@ -156,6 +179,9 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym = #var rd = handleSymbolFile(result) var rd: PRodReader result.flags = result.flags + flags + if sfMainModule in result.flags: + gMainPackageId = result.owner.id + if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: rd = handleSymbolFile(result) if result.id < 0: @@ -181,8 +207,11 @@ proc importModule*(s: PSym, fileIdx: int32): PSym {.procvar.} = # this is called by the semantic checking phase result = compileModule(fileIdx, {}) if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx) - if sfSystemModule in result.flags: - localError(result.info, errAttemptToRedefine, result.name.s) + #if sfSystemModule in result.flags: + # localError(result.info, errAttemptToRedefine, result.name.s) + # restore the notes for outer module: + gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes + else: ForeignPackageNotes proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} = result = syntaxes.parseFile(fileIdx) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index a556ad0c5..4a9980066 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -471,6 +471,8 @@ type shortName*: string # short name of the module quotedName*: Rope # cached quoted short name for codegen # purposes + quotedFullName*: Rope # cached quoted full name for codegen + # purposes lines*: seq[Rope] # the source code of the module # used for better error messages and @@ -505,7 +507,7 @@ const warnProveField, warnProveIndex, warnGcUnsafe, hintSuccessX, hintPath, hintConf, - hintProcessing, + hintProcessing, hintPattern, hintDependency, hintExecuting, hintLinking, hintCodeBegin, hintCodeEnd, @@ -514,9 +516,8 @@ const {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, warnProveField, warnProveIndex, warnGcUnsafe, - hintPath, hintConf, + hintPath, hintDependency, - hintExecuting, hintCodeBegin, hintCodeEnd, hintSource, hintStackTrace, hintGCStats}, @@ -527,6 +528,8 @@ const InvalidFileIDX* = int32(-1) var + ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic, + hintQuitCalled, hintExecuting} filenameToIndexTbl = initTable[string, int32]() fileInfos*: seq[TFileInfo] = @[] systemFileIdx*: int32 @@ -561,6 +564,7 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo = let fileName = projPath.extractFilename result.shortName = fileName.changeFileExt("") result.quotedName = fileName.makeCString + result.quotedFullName = fullPath.makeCString if optEmbedOrigSrc in gGlobalOptions or true: result.lines = @[] @@ -616,6 +620,7 @@ var gHintCounter*: int = 0 gWarnCounter*: int = 0 gErrorMax*: int = 1 # stop after gErrorMax errors + gMainPackageNotes*: TNoteKinds = NotesVerbosity[1] proc unknownLineInfo*(): TLineInfo = result.line = int16(-1) @@ -755,7 +760,7 @@ proc msgWriteln*(s: string, flags: MsgFlags = {}) = flushFile(stderr) macro callIgnoringStyle(theProc: typed, first: typed, - args: varargs[expr]): stmt = + args: varargs[typed]): untyped = let typForegroundColor = bindSym"ForegroundColor".getType let typBackgroundColor = bindSym"BackgroundColor".getType let typStyle = bindSym"Style".getType @@ -772,7 +777,7 @@ macro callIgnoringStyle(theProc: typed, first: typed, typ != typTerminalCmd: result.add(arg) -macro callStyledWriteLineStderr(args: varargs[expr]): stmt = +macro callStyledWriteLineStderr(args: varargs[typed]): untyped = result = newCall(bindSym"styledWriteLine") result.add(bindSym"stderr") for arg in children(args[0][1]): @@ -784,7 +789,7 @@ template callWritelnHook(args: varargs[string, `$`]) = s.add arg writelnHook s -template styledMsgWriteln*(args: varargs[expr]) = +template styledMsgWriteln*(args: varargs[typed]) = if not isNil(writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in gGlobalOptions: @@ -995,11 +1000,11 @@ proc internalError*(errMsg: string) = writeContext(unknownLineInfo()) rawMessage(errInternal, errMsg) -template assertNotNil*(e: expr): expr = +template assertNotNil*(e): untyped = if e == nil: internalError($instantiationInfo()) e -template internalAssert*(e: bool): stmt = +template internalAssert*(e: bool) = if not e: internalError($instantiationInfo()) proc addSourceLine*(fileIdx: int32, line: string) = @@ -1022,7 +1027,10 @@ proc sourceLine*(i: TLineInfo): Rope = proc quotedFilename*(i: TLineInfo): Rope = internalAssert i.fileIndex >= 0 - result = fileInfos[i.fileIndex].quotedName + if optExcessiveStackTrace in gGlobalOptions: + result = fileInfos[i.fileIndex].quotedFullName + else: + result = fileInfos[i.fileIndex].quotedName ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = case err diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 4f9962ea8..0ff128ba3 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -7,7 +7,7 @@ path:"$projectPath/.." path:"$lib/packages/docutils" define:booting -import:testability +#import:"$projectpath/testability" @if windows: cincludes: "$lib/wrappers/libffi/common" diff --git a/compiler/nim.nim b/compiler/nim.nim index b242052ec..a58afd593 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -56,12 +56,12 @@ proc handleCmdLine() = loadConfigs(DefaultConfig) # load all config files let scriptFile = gProjectFull.changeFileExt("nims") if fileExists(scriptFile): - runNimScript(scriptFile) + runNimScript(scriptFile, freshDefines=false) # 'nim foo.nims' means to just run the NimScript file and do nothing more: if scriptFile == gProjectFull: return elif fileExists(gProjectPath / "config.nims"): # directory wide NimScript file - runNimScript(gProjectPath / "config.nims") + runNimScript(gProjectPath / "config.nims", freshDefines=false) # now process command line arguments again, because some options in the # command line can overwite the config file's settings extccomp.initVars() diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg index 73219d6f8..0d9dbfa4b 100644 --- a/compiler/nimfix/nimfix.nim.cfg +++ b/compiler/nimfix/nimfix.nim.cfg @@ -5,7 +5,7 @@ hint[XDeclaredButNotUsed]:off path:"$projectPath/.." path:"$lib/packages/docutils" -path:"../../compiler" +path:"$nim" define:useStdoutAsStdmsg symbol:nimfix diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index 1123afb9e..8ba922927 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -66,7 +66,7 @@ proc beautifyName(s: string, k: TSymKind): string = ]: result.add s[i] else: - result.add toUpper(s[i]) + result.add toUpperAscii(s[i]) of skConst, skEnumField: # for 'const' we keep how it's spelt; either upper case or lower case: result.add s[0] @@ -74,7 +74,7 @@ proc beautifyName(s: string, k: TSymKind): string = # as a special rule, don't transform 'L' to 'l' if s.len == 1 and s[0] == 'L': result.add 'L' elif '_' in s: result.add(s[i]) - else: result.add toLower(s[0]) + else: result.add toLowerAscii(s[0]) inc i while i < s.len: if s[i] == '_': @@ -85,9 +85,9 @@ proc beautifyName(s: string, k: TSymKind): string = result.add s[i] else: inc i - result.add toUpper(s[i]) + result.add toUpperAscii(s[i]) elif allUpper: - result.add toLower(s[i]) + result.add toLowerAscii(s[i]) else: result.add s[i] inc i diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index f15ad6368..94507adf0 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -121,7 +121,7 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = e = b inc(e) -template nodeSetOp(a, b: PNode, op: expr) {.dirty.} = +template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} = var x, y: TBitSet toBitSet(a, x) toBitSet(b, y) diff --git a/compiler/options.nim b/compiler/options.nim index 2716a98d3..a5154cb48 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -67,6 +67,8 @@ type # please make sure we have under 32 options optIdeDebug # idetools: debug mode optIdeTerse # idetools: use terse descriptions optNoCppExceptions # use C exception handling even with CPP + optExcessiveStackTrace # fully qualified module filenames + TGlobalOptions* = set[TGlobalOption] TCommands* = enum # Nim's commands # **keep binary compatible** @@ -127,10 +129,10 @@ var proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc -template compilationCachePresent*: expr = +template compilationCachePresent*: untyped = {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} -template optPreserveOrigSource*: expr = +template optPreserveOrigSource*: untyped = optEmbedOrigSrc in gGlobalOptions const @@ -149,6 +151,7 @@ const var gConfigVars* = newStringTable(modeStyleInsensitive) gDllOverrides = newStringTable(modeCaseInsensitive) + gModuleOverrides* = newStringTable(modeStyleInsensitive) gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc. libpath* = "" gProjectName* = "" # holds a name like 'nim' @@ -209,9 +212,7 @@ proc setDefaultLibpath*() = # Special rule to support other tools (nimble) which import the compiler # modules and make use of them. - let realNimPath = # Make sure we expand the symlink - if symlinkExists(findExe("nim")): expandSymlink(findExe("nim")) - else: findExe("nim") + let realNimPath = findExe("nim") # Find out if $nim/../../lib/system.nim exists. let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib" if not fileExists(libpath / "system.nim") and @@ -219,7 +220,7 @@ proc setDefaultLibpath*() = libpath = parentNimLibPath proc canonicalizePath*(path: string): string = - when not FileSystemCaseSensitive: result = path.expandFilename.toLower + when not FileSystemCaseSensitive: result = path.expandFilename.toLowerAscii else: result = path.expandFilename proc shortenDir*(dir: string): string = @@ -238,57 +239,26 @@ proc removeTrailingDirSep*(path: string): string = else: result = path +include packagehandling + proc getNimcacheDir*: string = result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / genSubDir -template newPackageCache(): expr = - newStringTable(when FileSystemCaseSensitive: - modeCaseInsensitive - else: - modeCaseSensitive) - -var packageCache = newPackageCache() - -proc resetPackageCache*() = packageCache = newPackageCache() - -iterator myParentDirs(p: string): string = - # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug... - var current = p - while true: - current = current.parentDir - if current.len == 0: break - yield current - -proc getPackageName*(path: string): string = - var parents = 0 - block packageSearch: - for d in myParentDirs(path): - if packageCache.hasKey(d): - #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name - return packageCache[d] - inc parents - for file in walkFiles(d / "*.nimble"): - result = file.splitFile.name - break packageSearch - for file in walkFiles(d / "*.babel"): - result = file.splitFile.name - break packageSearch - # we also store if we didn't find anything: - if result.isNil: result = "" - for d in myParentDirs(path): - #echo "set cache ", d, " |", result, "|", parents - packageCache[d] = result - dec parents - if parents <= 0: break - -proc withPackageName*(path: string): string = - let x = path.getPackageName - if x.len == 0: - result = path - else: - let (p, file, ext) = path.splitFile - result = (p / (x & '_' & file)) & ext + +proc pathSubs*(p, config: string): string = + let home = removeTrailingDirSep(os.getHomeDir()) + result = unixToNativePath(p % [ + "nim", getPrefixDir(), + "lib", libpath, + "home", home, + "config", config, + "projectname", options.gProjectName, + "projectpath", options.gProjectPath, + "projectdir", options.gProjectPath, + "nimcache", getNimcacheDir()]) + if '~' in result: + result = result.replace("~", home) proc toGeneratedFile*(path, ext: string): string = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" @@ -357,17 +327,25 @@ proc rawFindFile2(f: string): string = it = PStrEntry(it.next) result = "" +template patchModule() {.dirty.} = + if result.len > 0 and gModuleOverrides.len > 0: + let key = getPackageName(result) & "_" & splitFile(result).name + if gModuleOverrides.hasKey(key): + let ov = gModuleOverrides[key] + if ov.len > 0: result = ov + proc findFile*(f: string): string {.procvar.} = if f.isAbsolute: result = if f.existsFile: f else: "" else: result = f.rawFindFile if result.len == 0: - result = f.toLower.rawFindFile + result = f.toLowerAscii.rawFindFile if result.len == 0: result = f.rawFindFile2 if result.len == 0: - result = f.toLower.rawFindFile2 + result = f.toLowerAscii.rawFindFile2 + patchModule() proc findModule*(modulename, currentModule: string): string = # returns path to module @@ -386,6 +364,7 @@ proc findModule*(modulename, currentModule: string): string = result = currentPath / m if not existsFile(result): result = findFile(m) + patchModule() proc libCandidates*(s: string, dest: var seq[string]) = var le = strutils.find(s, '(') @@ -426,10 +405,10 @@ proc binaryStrSearch*(x: openArray[string], y: string): int = return mid result = - 1 -template nimdbg*: expr = c.module.fileIdx == gProjectMainIdx -template cnimdbg*: expr = p.module.module.fileIdx == gProjectMainIdx -template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx -template lnimdbg*: expr = L.fileIdx == gProjectMainIdx +template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx +template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx +template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx +template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx proc parseIdeCmd*(s: string): IdeCmd = case s: diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim new file mode 100644 index 000000000..6a98ffe60 --- /dev/null +++ b/compiler/packagehandling.nim @@ -0,0 +1,56 @@ +# +# +# The Nim Compiler +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +iterator myParentDirs(p: string): string = + # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug... + var current = p + while true: + current = current.parentDir + if current.len == 0: break + yield current + +template newPackageCache(): untyped = + newStringTable(when FileSystemCaseSensitive: + modeCaseInsensitive + else: + modeCaseSensitive) + +var packageCache = newPackageCache() + +proc resetPackageCache*() = packageCache = newPackageCache() + +proc getPackageName*(path: string): string = + var parents = 0 + block packageSearch: + for d in myParentDirs(path): + if packageCache.hasKey(d): + #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name + return packageCache[d] + inc parents + for file in walkFiles(d / "*.nimble"): + result = file.splitFile.name + break packageSearch + for file in walkFiles(d / "*.babel"): + result = file.splitFile.name + break packageSearch + # we also store if we didn't find anything: + if result.isNil: result = "" + for d in myParentDirs(path): + #echo "set cache ", d, " |", result, "|", parents + packageCache[d] = result + dec parents + if parents <= 0: break + +proc withPackageName*(path: string): string = + let x = path.getPackageName + if x.len == 0: + result = path + else: + let (p, file, ext) = path.splitFile + result = (p / (x & '_' & file)) & ext diff --git a/compiler/parser.nim b/compiler/parser.nim index f22177ac1..19ef0960a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -98,7 +98,7 @@ proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = ## Produce and emit a parser message to output about the token `tok` parMessage(p, msg, prettyTok(tok)) -template withInd(p: expr, body: stmt) {.immediate.} = +template withInd(p, body: untyped) = let oldInd = p.currInd p.currInd = p.tok.indent body @@ -202,7 +202,7 @@ proc isRightAssociative(tok: TToken): bool {.inline.} = proc getPrecedence(tok: TToken, strongSpaces: bool): int = ## Calculates the precedence of the given token. - template considerStrongSpaces(x): expr = + template considerStrongSpaces(x): untyped = x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0) case tok.tokType @@ -214,7 +214,7 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int = if L > 1 and tok.ident.s[L-1] == '>' and tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1) - template considerAsgn(value: expr) = + template considerAsgn(value: untyped) = result = if tok.ident.s[L-1] == '=': 1 else: value case relevantChar @@ -326,6 +326,7 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode = getTok(p) else: parMessage(p, errIdentifierExpected, p.tok) + break eat(p, tkAccent) else: if allowNil and p.tok.tokType == tkNil: @@ -685,11 +686,19 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = #| | '{' optInd indexExprList optPar '}' #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax result = r + + template somePar() = + if p.tok.strongSpaceA > 0: + if p.strongSpaces: + break + else: + parMessage(p, warnDeprecated, + "a [b] will be parsed as command syntax; spacing") while p.tok.indent < 0 or (p.tok.tokType == tkDot and p.tok.indent >= baseIndent): case p.tok.tokType of tkParLe: - if p.strongSpaces and p.tok.strongSpaceA > 0: break + somePar() result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr @@ -704,10 +713,10 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = result = dotExpr(p, result) result = parseGStrLit(p, result) of tkBracketLe: - if p.strongSpaces and p.tok.strongSpaceA > 0: break + somePar() result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: - if p.strongSpaces and p.tok.strongSpaceA > 0: break + somePar() result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType: if p.inPragma == 0: @@ -963,8 +972,9 @@ proc parseDoBlock(p: var TParser): PNode = proc parseDoBlocks(p: var TParser, call: PNode) = #| doBlocks = doBlock ^* IND{=} if p.tok.tokType == tkDo: - addSon(call, parseDoBlock(p)) - while sameInd(p) and p.tok.tokType == tkDo: + #withInd(p): + # addSon(call, parseDoBlock(p)) + while sameOrNoInd(p) and p.tok.tokType == tkDo: addSon(call, parseDoBlock(p)) proc parseProcExpr(p: var TParser, isExpr: bool): PNode = diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 9b9fdca4e..d4361a671 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -24,7 +24,7 @@ proc verboseProcess(context: PPassContext, n: PNode): PNode = # system.nim deactivates all hints, for verbosity:3 we want the processing # messages nonetheless, so we activate them again unconditionally: incl(msgs.gNotes, hintProcessing) - message(n.info, hintProcessing, $idgen.gBackendId) + message(n.info, hintProcessing, $idgen.gFrontendId) const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 604d3521d..09b8d3305 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -75,7 +75,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = result = matchNodeKinds(p.constraint, n) if not result: return if isNil(n.typ): - result = p.typ.kind in {tyEmpty, tyStmt} + result = p.typ.kind in {tyVoid, tyStmt} else: result = sigmatch.argtypeMatches(c.c, p.typ, n.typ) @@ -129,7 +129,7 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool = result = bindOrCheck(c, p.sons[2].sym, arglist) proc matches(c: PPatternContext, p, n: PNode): bool = - # hidden conversions (?) + let n = skipHidden(n) if nfNoRewrite in n.flags: result = false elif isPatternParam(c, p): diff --git a/compiler/platform.nim b/compiler/platform.nim index dc414bfeb..36ec10477 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -159,7 +159,7 @@ type # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, - cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430 + cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64 type TEndian* = enum @@ -187,7 +187,8 @@ const (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), - (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)] + (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), + (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64)] var targetCPU*, hostCPU*: TSystemCPU diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 2280ef712..db5f8e727 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -63,11 +63,12 @@ const wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, - wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims} + wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, + wIntDefine, wStrDefine} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} - allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas + allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) # implementation @@ -89,43 +90,38 @@ proc pragmaAsm*(c: PContext, n: PNode): char = else: invalidPragma(it) -proc setExternName(s: PSym, extname: string) = - s.loc.r = rope(extname % s.name.s) +proc setExternName(s: PSym, extname: string, info: TLineInfo) = + # special cases to improve performance: + if extname == "$1": + s.loc.r = rope(s.name.s) + elif '$' notin extname: + s.loc.r = rope(extname) + else: + try: + s.loc.r = rope(extname % s.name.s) + except ValueError: + localError(info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") if gCmd == cmdPretty and '$' notin extname: # note that '{.importc.}' is transformed into '{.importc: "$1".}' s.loc.flags.incl(lfFullExternalName) -proc makeExternImport(s: PSym, extname: string) = - setExternName(s, extname) +proc makeExternImport(s: PSym, extname: string, info: TLineInfo) = + setExternName(s, extname, info) incl(s.flags, sfImportc) excl(s.flags, sfForward) -proc validateExternCName(s: PSym, info: TLineInfo) = - ## Validates that the symbol name in s.loc.r is a valid C identifier. - ## - ## Valid identifiers are those alphanumeric including the underscore not - ## starting with a number. If the check fails, a generic error will be - ## displayed to the user. - let target = $s.loc.r - if target.len < 1 or target[0] notin IdentStartChars or - not target.allCharsInSet(IdentChars): - localError(info, errGenerated, "invalid exported symbol") - proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname) - # XXX to fix make it work with nimrtl. - #if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}: - # validateExternCName(s, info) + setExternName(s, extname, info) incl(s.flags, sfExportc) -proc processImportCompilerProc(s: PSym, extname: string) = - setExternName(s, extname) +proc processImportCompilerProc(s: PSym, extname: string, info: TLineInfo) = + setExternName(s, extname, info) incl(s.flags, sfImportc) excl(s.flags, sfForward) incl(s.loc.flags, lfImportCompilerProc) -proc processImportCpp(s: PSym, extname: string) = - setExternName(s, extname) +proc processImportCpp(s: PSym, extname: string, info: TLineInfo) = + setExternName(s, extname, info) incl(s.flags, sfImportc) incl(s.flags, sfInfixCall) excl(s.flags, sfForward) @@ -133,8 +129,8 @@ proc processImportCpp(s: PSym, extname: string) = incl(m.flags, sfCompileToCpp) extccomp.gMixedMode = true -proc processImportObjC(s: PSym, extname: string) = - setExternName(s, extname) +proc processImportObjC(s: PSym, extname: string, info: TLineInfo) = + setExternName(s, extname, info) incl(s.flags, sfImportc) incl(s.flags, sfNamedParamCall) excl(s.flags, sfForward) @@ -256,8 +252,9 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode = proc processDynLib(c: PContext, n: PNode, sym: PSym) = if (sym == nil) or (sym.kind == skModule): - POptionEntry(c.optionStack.tail).dynlib = getLib(c, libDynamic, - expectDynlibNode(c, n)) + let lib = getLib(c, libDynamic, expectDynlibNode(c, n)) + if not lib.isOverriden: + POptionEntry(c.optionStack.tail).dynlib = lib else: if n.kind == nkExprColonExpr: var lib = getLib(c, libDynamic, expectDynlibNode(c, n)) @@ -276,6 +273,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = proc processNote(c: PContext, n: PNode) = if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and (n.sons[0].kind == nkBracketExpr) and + (n.sons[0].sons.len == 2) and (n.sons[0].sons[1].kind == nkIdent) and (n.sons[0].sons[0].kind == nkIdent): #and (n.sons[1].kind == nkIdent): @@ -391,21 +389,25 @@ type TLinkFeature = enum linkNormal, linkSys -proc processCompile(c: PContext, n: PNode) = +proc relativeFile(c: PContext; n: PNode; ext=""): string = var s = expectStrLit(c, n) - var found = findFile(s) - if found == "": found = s - var trunc = changeFileExt(found, "") - if not isAbsolute(found): - found = parentDir(n.info.toFullPath) / found + if ext.len > 0 and splitFile(s).ext == "": + s = addFileExt(s, ext) + result = parentDir(n.info.toFullPath) / s + if not fileExists(result): + if isAbsolute(s): result = s + else: + result = findFile(s) + if result.len == 0: result = s + +proc processCompile(c: PContext, n: PNode) = + let found = relativeFile(c, n) + let trunc = found.changeFileExt("") extccomp.addExternalFileToCompile(found) extccomp.addFileToLink(completeCFilePath(trunc, false)) proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) = - var f = expectStrLit(c, n) - if splitFile(f).ext == "": f = addFileExt(f, CC[cCompiler].objExt) - var found = findFile(f) - if found == "": found = f # use the default + let found = relativeFile(c, n, CC[cCompiler].objExt) case feature of linkNormal: extccomp.addFileToLink(found) of linkSys: @@ -564,7 +566,7 @@ proc deprecatedStmt(c: PContext; pragma: PNode) = localError(pragma.info, "list of key:value pairs expected"); return for n in pragma: if n.kind in {nkExprColonExpr, nkExprEqExpr}: - let dest = qualifiedLookUp(c, n[1]) + let dest = qualifiedLookUp(c, n[1], {checkUndeclared}) let src = considerQuotedIdent(n[0]) let alias = newSym(skAlias, src, dest, n[0].info) incl(alias.flags, sfExported) @@ -589,7 +591,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = # and perform the lookup on demand instead. result = newSym(skUnknown, considerQuotedIdent(n), nil, n.info) else: - result = qualifiedLookUp(c, n) + result = qualifiedLookUp(c, n, {checkUndeclared}) proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, validPragmas: TSpecialWords): bool = @@ -616,20 +618,23 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, of wExportc: makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) incl(sym.flags, sfUsed) # avoid wrong hints - of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) + of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"), it.info) of wImportCompilerProc: - processImportCompilerProc(sym, getOptionalStr(c, it, "$1")) - of wExtern: setExternName(sym, expectStrLit(c, it)) + processImportCompilerProc(sym, getOptionalStr(c, it, "$1"), it.info) + of wExtern: setExternName(sym, expectStrLit(c, it), it.info) of wImmediate: - if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) + if sym.kind in {skTemplate, skMacro}: + incl(sym.flags, sfImmediate) + incl(sym.flags, sfAllUntyped) + message(n.info, warnDeprecated, "use 'untyped' parameters instead; immediate") else: invalidPragma(it) of wDirty: if sym.kind == skTemplate: incl(sym.flags, sfDirty) else: invalidPragma(it) of wImportCpp: - processImportCpp(sym, getOptionalStr(c, it, "$1")) + processImportCpp(sym, getOptionalStr(c, it, "$1"), it.info) of wImportObjC: - processImportObjC(sym, getOptionalStr(c, it, "$1")) + processImportObjC(sym, getOptionalStr(c, it, "$1"), it.info) of wAlign: if sym.typ == nil: invalidPragma(it) var align = expectIntLit(c, it) @@ -894,6 +899,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, of wBase: noVal(it) sym.flags.incl sfBase + of wIntDefine: + sym.magic = mIntDefine + of wStrDefine: + sym.magic = mStrDefine else: invalidPragma(it) else: invalidPragma(it) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 03f6d4832..0e733d643 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -704,7 +704,10 @@ proc gcase(g: var TSrcGen, n: PNode) = proc gproc(g: var TSrcGen, n: PNode) = var c: TContext if n.sons[namePos].kind == nkSym: - put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym)) + let s = n.sons[namePos].sym + put(g, tkSymbol, renderDefinitionName(s)) + if sfGenSym in s.flags: + put(g, tkIntLit, $s.id) else: gsub(g, n.sons[namePos]) @@ -712,7 +715,11 @@ proc gproc(g: var TSrcGen, n: PNode) = gpattern(g, n.sons[patternPos]) let oldCheckAnon = g.checkAnon g.checkAnon = true - gsub(g, n.sons[genericParamsPos]) + if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and + n[miscPos][1].kind != nkEmpty: + gsub(g, n[miscPos][1]) + else: + gsub(g, n.sons[genericParamsPos]) g.checkAnon = oldCheckAnon gsub(g, n.sons[paramsPos]) gsub(g, n.sons[pragmasPos]) @@ -794,7 +801,8 @@ proc gident(g: var TSrcGen, n: PNode) = else: t = tkOpr put(g, t, s) - if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id) + if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags): + put(g, tkIntLit, $n.sym.id) proc doParamsAux(g: var TSrcGen, params: PNode) = if params.len > 1: @@ -802,7 +810,7 @@ proc doParamsAux(g: var TSrcGen, params: PNode) = gsemicolon(g, params, 1) put(g, tkParRi, ")") - if params.sons[0].kind != nkEmpty: + if params.len > 0 and params.sons[0].kind != nkEmpty: putWithSpace(g, tkOpr, "->") gsub(g, params.sons[0]) diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 91f93d279..d24e8f560 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -21,7 +21,7 @@ proc toStrMaxPrecision*(f: BiggestFloat): string = if f > 0.0: result = "INF" else: result = "-INF" else: - var buf: array [0..80, char] + var buf: array[0..80, char] c_sprintf(buf, "%#.16e", f) result = $buf @@ -64,7 +64,7 @@ const const vintDelta = 5 -template encodeIntImpl(self: expr) = +template encodeIntImpl(self) = var d: char var v = x var rem = v mod 190 diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 22cd282fd..8b2653bc9 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -13,10 +13,10 @@ import ast, modules, passes, passaux, condsyms, options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs, - os, times, osproc + os, times, osproc, wordrecg, strtabs # we support 'cmpIgnoreStyle' natively for efficiency: -from strutils import cmpIgnoreStyle +from strutils import cmpIgnoreStyle, contains proc listDirs(a: VmArgs, filter: set[PathComponent]) = let dir = getString(a, 0) @@ -116,12 +116,27 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext = setResult(a, options.command) cbconf switch: processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo()) - - -proc runNimScript*(scriptName: string) = + cbconf hintImpl: + processSpecificNote(a.getString 0, wHint, passPP, unknownLineInfo(), + a.getString 1) + cbconf warningImpl: + processSpecificNote(a.getString 0, wWarning, passPP, unknownLineInfo(), + a.getString 1) + cbconf patchFile: + let key = a.getString(0) & "_" & a.getString(1) + var val = a.getString(2).addFileExt(NimExt) + if {'$', '~'} in val: + val = pathSubs(val, vthisDir) + elif not isAbsolute(val): + val = vthisDir / val + gModuleOverrides[key] = val + cbconf selfExe: + setResult(a, os.getAppFilename()) + +proc runNimScript*(scriptName: string; freshDefines=true) = passes.gIncludeFile = includeModule passes.gImportModule = importModule - initDefines() + if freshDefines: initDefines() defineSymbol("nimscript") defineSymbol("nimconfig") diff --git a/compiler/sem.nim b/compiler/sem.nim index 97a20a4da..7db4ae47e 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -59,21 +59,11 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; # templates perform some quick check whether the cursor is actually in # the generic or template. when defined(nimsuggest): - assert gCmd == cmdIdeTools - if requiresCheck: + if gCmd == cmdIdeTools and requiresCheck: #if optIdeDebug in gGlobalOptions: # echo "passing to safeSemExpr: ", renderTree(n) discard safeSemExpr(c, n) -proc typeMismatch(n: PNode, formal, actual: PType) = - if formal.kind != tyError and actual.kind != tyError: - let named = typeToString(formal) - let desc = typeToString(formal, preferDesc) - let x = if named == desc: named else: named & " = " & desc - localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & - typeToString(actual) & ") " & - `%`(msgKindToString(errButExpectedX), [x])) - proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = if arg.typ.isNil: localError(arg.info, errExprXHasNoType, @@ -174,7 +164,7 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = - proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower + proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii # like newSymS, but considers gensym'ed symbols if n.kind == nkSym: @@ -316,6 +306,13 @@ proc semConstExpr(c: PContext, n: PNode): PNode = include hlo, seminst, semcall +when false: + # hopefully not required: + proc resetSemFlag(n: PNode) = + excl n.flags, nfSem + for i in 0 ..< n.safeLen: + resetSemFlag(n[i]) + proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = ## Semantically check the output of a macro. @@ -329,6 +326,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, c.friendModules.add(s.owner.getModule) result = n + excl(n.flags, nfSem) + #resetSemFlag n if s.typ.sons[0] == nil: result = semStmt(c, result) else: @@ -363,7 +362,6 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.module, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, sym, flags) @@ -419,17 +417,42 @@ proc myOpen(module: PSym): PPassContext = c.importTable.addSym(module) # a module knows itself if sfSystemModule in module.flags: magicsys.systemModule = module # set global variable! - else: - c.importTable.addSym magicsys.systemModule # import the "System" identifier - importAllSymbols(c, magicsys.systemModule) c.topLevelScope = openScope(c) + # don't be verbose unless the module belongs to the main package: + if module.owner.id == gMainPackageId: + gNotes = gMainPackageNotes + else: + if gMainPackageNotes == {}: gMainPackageNotes = gNotes + gNotes = ForeignPackageNotes result = c proc myOpenCached(module: PSym, rd: PRodReader): PPassContext = result = myOpen(module) for m in items(rd.methods): methodDef(m, true) +proc isImportSystemStmt(n: PNode): bool = + if magicsys.systemModule == nil: return false + case n.kind + of nkImportStmt: + for x in n: + let f = checkModuleName(x) + if f == magicsys.systemModule.info.fileIndex: + return true + of nkImportExceptStmt, nkFromStmt: + let f = checkModuleName(n[0]) + if f == magicsys.systemModule.info.fileIndex: + return true + else: discard + proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = + if c.topStmts == 0 and not isImportSystemStmt(n): + if sfSystemModule notin c.module.flags and + n.kind notin {nkEmpty, nkCommentStmt}: + c.importTable.addSym magicsys.systemModule # import the "System" identifier + importAllSymbols(c, magicsys.systemModule) + inc c.topStmts + else: + inc c.topStmts if sfNoForward in c.module.flags: result = semAllTypeSections(c, n) else: diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index a1e209263..9702128ba 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -183,7 +183,7 @@ proc newSeqCall(c: PContext; x, y: PNode): PNode = proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind - of tyNone, tyEmpty: discard + of tyNone, tyEmpty, tyVoid: discard of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPtr, tyString, tyRef: defaultOp(c, t, body, x, y) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 916d897c9..97209167d 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -53,7 +53,18 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, if symx.kind in filter: syms.add((symx, o.lastOverloadScope)) symx = nextOverloadIter(o, c, headSymbol) - if syms.len == 0: return + if syms.len == 0: + when false: + if skIterator notin filter: + # also try iterators, but these are 2nd class: + symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind == skIterator: + syms.add((symx, 100)) + symx = nextOverloadIter(o, c, headSymbol) + if syms.len == 0: return + else: + return var z: TCandidate initCandidate(c, best, syms[0][0], initialBinding, symScope) @@ -69,7 +80,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, # gDebug = true matches(c, n, orig, z) if errors != nil: - errors.safeAdd(sym) + errors.safeAdd((sym, int z.mutabilityProblem)) if z.errors != nil: for err in z.errors: errors.add(err) @@ -111,7 +122,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = let proto = describeArgs(c, n, 1, preferName) var prefer = preferName - for err in errors: + for err, mut in items(errors): var errProto = "" let n = err.typ.n for i in countup(1, n.len - 1): @@ -123,14 +134,21 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = if errProto == proto: prefer = preferModuleInfo break + # now use the information stored in 'prefer' to produce a nice error message: var result = msgKindToString(errTypeMismatch) add(result, describeArgs(c, n, 1, prefer)) add(result, ')') var candidates = "" - for err in errors: - add(candidates, err.getProcHeader(prefer)) + for err, mut in items(errors): + if err.kind in routineKinds and err.ast != nil: + add(candidates, renderTree(err.ast, + {renderNoBody, renderNoComments,renderNoPragmas})) + else: + add(candidates, err.getProcHeader(prefer)) add(candidates, "\n") + if mut != 0 and mut < n.len: + add(candidates, "for a 'var' type a variable needs to be passed, but '" & renderTree(n[mut]) & "' is immutable\n") if candidates != "": add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates) if c.compilesContextId > 0 and optReportConceptFailures in gGlobalOptions: @@ -138,6 +156,20 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = else: localError(n.info, errGenerated, result) +proc bracketNotFoundError(c: PContext; n: PNode) = + var errors: CandidateErrors = @[] + var o: TOverloadIter + let headSymbol = n[0] + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in routineKinds: + errors.add((symx, 0)) + symx = nextOverloadIter(o, c, headSymbol) + if errors.len == 0: + localError(n.info, "could not resolve: " & $n) + else: + notFoundError(c, n, errors) + proc resolveOverloads(c: PContext, n, orig: PNode, filter: TSymKinds; errors: var CandidateErrors): TCandidate = @@ -154,8 +186,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode, template pickBest(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, filter, result, alt, errors) - - pickBest(f) let overloadsState = result.state @@ -226,7 +256,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode, #notFoundError(c, n, errors) return - if alt.state == csMatch and cmpCandidates(result, alt) == 0 and not sameMethodDispatcher(result.calleeSym, alt.calleeSym): internalAssert result.state == csMatch @@ -295,8 +324,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = var finalCallee = x.calleeSym markUsed(n.sons[0].info, finalCallee) styleCheckUse(n.sons[0].info, finalCallee) - if finalCallee.ast == nil: - internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check! + assert finalCallee.ast != nil if x.hasFauxMatch: result = x.call result.sons[0] = newSymNode(finalCallee, result.sons[0].info) @@ -315,12 +343,12 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = # are added as normal params. for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind - of skConst: - x.call.add s.ast - of skType: - x.call.add newSymNode(s, n.info) - else: - internalAssert false + of skConst: + x.call.add s.ast + of skType: + x.call.add newSymNode(s, n.info) + else: + internalAssert false result = x.call instGenericConvertersSons(c, result, x) @@ -361,7 +389,14 @@ proc explicitGenericInstError(n: PNode): PNode = proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = var m: TCandidate - initCandidate(c, m, s, n) + # binding has to stay 'nil' for this to work! + initCandidate(c, m, s, nil) + + 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) + if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) markUsed(n.info, s) styleCheckUse(n.info, s) @@ -382,6 +417,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = "; got " & $(n.len-1) & " type(s) but expected " & $expected) return n result = explicitGenericSym(c, n, s) + if result == nil: result = explicitGenericInstError(n) elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}: # choose the generic proc with the proper number of type parameters. # XXX I think this could be improved by reusing sigmatch.paramTypesMatch. @@ -394,11 +430,12 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = # it suffices that the candidate has the proper number of generic # type parameters: if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1: - result.add(explicitGenericSym(c, n, candidate)) + let x = explicitGenericSym(c, n, candidate) + if x != nil: result.add(x) # get rid of nkClosedSymChoice if not ambiguous: if result.len == 1 and a.kind == nkClosedSymChoice: result = result[0] - # candidateCount != 1: return explicitGenericInstError(n) + elif result.len == 0: result = explicitGenericInstError(n) else: result = explicitGenericInstError(n) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index a13f2c232..30b6e261d 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -47,7 +47,7 @@ type efLValue, efWantIterator, efInTypeof, efWantStmt, efAllowStmt, efDetermineType, efAllowDestructor, efWantValue, efOperand, efNoSemCheck, - efNoProcvarCheck + efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo TExprFlags* = set[TExprFlag] TTypeAttachedOp* = enum @@ -99,11 +99,12 @@ type unknownIdents*: IntSet # ids of all unknown identifiers to prevent # naming it multiple times generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile + topStmts*: int # counts the number of encountered top level statements lastGenericIdx*: int # used for the generics stack hloLoopDetector*: int # used to prevent endless loops in the HLO inParallelStmt*: int instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo; - op: TTypeAttachedOp): PSym {.nimcall.} + op: TTypeAttachedOp; col: int): PSym {.nimcall.} selfName*: PIdent signatures*: TStrTable diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index 1261dd460..9ea581f3a 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -87,7 +87,7 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = result = nil proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode = - template maybeAddLine(e: expr): stmt = + template maybeAddLine(e) = let stmt = e if stmt != nil: if result == nil: result = newNode(nkStmtList) @@ -139,6 +139,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType = t = t.skipTypes({tyGenericInst}) case t.kind of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: + t.destructor = analyzingDestructor if instantiateDestructor(c, t.sons[0]) != nil: t.destructor = getCompilerProc"nimDestroyRange" return t diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 16b4ee479..fc31829ba 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -15,7 +15,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, markUsed(n.info, s) styleCheckUse(n.info, s) pushInfoContext(n.info) - result = evalTemplate(n, s, getCurrOwner()) + result = evalTemplate(n, s, getCurrOwner(), efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags) popInfoContext() @@ -24,16 +24,15 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags + {efOperand}) - if result.kind == nkEmpty and result.typ.isNil: + #if result.kind == nkEmpty and result.typ.isNil: # do not produce another redundant error message: #raiseRecoverableError("") - result = errorNode(c, n) + # result = errorNode(c, n) if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind == tyVar: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: - result.typ = newTypeS(tyEmpty, c) - result.typ.flags.incl tfVoid + result.typ = newTypeS(tyVoid, c) else: localError(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) @@ -51,7 +50,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = renderTree(result, {renderNoComments})) result.typ = errorType(c) else: - # XXX tyGenericInst here? if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) semDestructorCheck(c, result, flags) @@ -74,8 +72,12 @@ proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = result = copyTree(s.ast) - result.typ = s.typ - result.info = n.info + if result.isNil: + localError(n.info, "constant of type '" & typeToString(s.typ) & "' has no value") + result = newSymNode(s) + else: + result.typ = s.typ + result.info = n.info type TConvStatus = enum @@ -83,8 +85,9 @@ type convNotNeedeed, convNotLegal -proc checkConversionBetweenObjects(castDest, src: PType): TConvStatus = - return if inheritanceDiff(castDest, src) == high(int): +proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvStatus = + let diff = inheritanceDiff(castDest, src) + return if diff == high(int) or (pointers > 1 and diff != 0): convNotLegal else: convOK @@ -101,13 +104,15 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = return var d = skipTypes(castDest, abstractVar) var s = skipTypes(src, abstractVar-{tyTypeDesc}) + var pointers = 0 while (d != nil) and (d.kind in {tyPtr, tyRef}) and (d.kind == s.kind): d = d.lastSon s = s.lastSon + inc pointers if d == nil: result = convNotLegal elif d.kind == tyObject and s.kind == tyObject: - result = checkConversionBetweenObjects(d, s) + result = checkConversionBetweenObjects(d, s, pointers) elif (skipTypes(castDest, abstractVarRange).kind in IntegralTypes) and (skipTypes(src, abstractVarRange-{tyTypeDesc}).kind in IntegralTypes): # accept conversion between integral types @@ -186,17 +191,17 @@ proc semConv(c: PContext, n: PNode): PNode = case status of convOK: # handle SomeProcType(SomeGenericProc) - # XXX: This needs fixing. checkConvertible uses typeRel internally, but - # doesn't bother to perform the work done in paramTypeMatchAux/fitNode - # so we are redoing the typeRel work here. Why does semConv exist as a - # separate proc from fitNode? if op.kind == nkSym and op.sym.isGenericRoutine: result.sons[1] = fitNode(c, result.typ, result.sons[1]) + elif op.kind == nkPar and targetType.kind == tyTuple: + op = fitNode(c, targetType, op) of convNotNeedeed: message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: - localError(n.info, errGenerated, msgKindToString(errIllegalConvFromXtoY)% - [op.typ.typeToString, result.typ.typeToString]) + result = fitNode(c, result.typ, result.sons[1]) + if result == nil: + localError(n.info, errGenerated, msgKindToString(errIllegalConvFromXtoY)% + [op.typ.typeToString, result.typ.typeToString]) else: for i in countup(0, sonsLen(op) - 1): let it = op.sons[i] @@ -206,7 +211,7 @@ proc semConv(c: PContext, n: PNode): PNode = styleCheckUse(n.info, it.sym) markIndirect(c, it.sym) return it - localError(n.info, errUseQualifier, op.sons[0].sym.name.s) + errorUseQualifier(c, n.info, op.sons[0].sym) proc semCast(c: PContext, n: PNode): PNode = ## Semantically analyze a casting ("cast[type](param)") @@ -594,6 +599,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = result = n if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return var callee = n.sons[0].sym + # workaround for bug #537 (overly aggressive inlining leading to + # wrong NimNode semantics): + if n.typ != nil and tfTriggersCompileTime in n.typ.flags: return # constant folding that is necessary for correctness of semantic pass: if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil: @@ -710,25 +718,60 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode; initCandidate(c, result, t) matches(c, n, nOrig, result) +proc bracketedMacro(n: PNode): PSym = + if n.len >= 1 and n[0].kind == nkSym: + result = n[0].sym + 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 semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil checkMinSonsLen(n, 1) var prc = n.sons[0] if n.sons[0].kind == nkDotExpr: checkSonsLen(n.sons[0], 2) - n.sons[0] = semFieldAccess(c, n.sons[0]) - if n.sons[0].kind == nkDotCall: + let n0 = semFieldAccess(c, n.sons[0]) + if n0.kind == nkDotCall: # it is a static call! - result = n.sons[0] + result = n0 result.kind = nkCall result.flags.incl nfExplicitCall for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) return semExpr(c, result, flags) + else: + n.sons[0] = n0 else: - n.sons[0] = semExpr(c, n.sons[0]) + n.sons[0] = semExpr(c, n.sons[0], {efInCall}) let t = n.sons[0].typ if t != nil and t.kind == tyVar: n.sons[0] = newDeref(n.sons[0]) + elif n.sons[0].kind == nkBracketExpr: + let s = bracketedMacro(n.sons[0]) + if s != nil: + return semBracketedMacro(c, n, n.sons[0], s, flags) + let nOrig = n.copyTree semOpAux(c, n) var t: PType = nil @@ -958,8 +1001,20 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = else: result = newSymNode(s, n.info) else: result = newSymNode(s, n.info) - of skMacro: result = semMacroExpr(c, n, n, s, flags) - of skTemplate: result = semTemplateExpr(c, n, s, flags) + of skMacro: + if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: + markUsed(n.info, s) + styleCheckUse(n.info, s) + result = newSymNode(s, n.info) + else: + result = semMacroExpr(c, n, n, s, flags) + of skTemplate: + if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: + markUsed(n.info, s) + styleCheckUse(n.info, s) + result = newSymNode(s, n.info) + else: + result = semTemplateExpr(c, n, s, flags) of skParam: markUsed(n.info, s) styleCheckUse(n.info, s) @@ -1011,8 +1066,11 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) of skField: - if c.p != nil and c.p.selfSym != nil: - var ty = skipTypes(c.p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef}) + var p = c.p + while p != nil and p.selfSym == nil: + p = p.next + if p != nil and p.selfSym != nil: + var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) var check: PNode = nil if ty.kind == tyObject: @@ -1025,7 +1083,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = markUsed(n.info, f) styleCheckUse(n.info, f) result = newNodeIT(nkDotExpr, n.info, f.typ) - result.add makeDeref(newSymNode(c.p.selfSym)) + result.add makeDeref(newSymNode(p.selfSym)) result.add newSymNode(f) # we now have the correct field if check != nil: check.sons[0] = result @@ -1051,7 +1109,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # here at all! #if isSymChoice(n.sons[1]): return - var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) + var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule}) if s != nil: if s.kind in OverloadableSyms: result = symChoice(c, n, s, scClosed) @@ -1186,7 +1244,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result.add(x[0]) return checkMinSonsLen(n, 2) - n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck}) + # make sure we don't evaluate generic macros/templates + n.sons[0] = semExprWithType(c, n.sons[0], + {efNoProcvarCheck, efNoEvaluateGeneric}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, @@ -1229,12 +1289,30 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = let s = if n.sons[0].kind == nkSym: n.sons[0].sym elif n[0].kind in nkSymChoices: n.sons[0][0].sym else: nil - if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}: - # type parameters: partial generic specialization - n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) - result = explicitGenericInstantiation(c, n, s) - elif s != nil and s.kind == skType: - result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + if s != nil: + case s.kind + of skProc, skMethod, skConverter, skIterator: + # type parameters: partial generic specialization + n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) + result = explicitGenericInstantiation(c, n, s) + of skMacro, skTemplate: + if efInCall in flags: + # We are processing macroOrTmpl[] in macroOrTmpl[](...) call. + # Return as is, so it can be transformed into complete macro or + # template call in semIndirectOp caller. + result = n + else: + # We are processing macroOrTmpl[] not in call. Transform it to the + # macro or template call with generic arguments here. + n.kind = nkCall + case s.kind + of skMacro: result = semMacroExpr(c, n, n, s, flags) + of skTemplate: result = semTemplateExpr(c, n, s, flags) + else: discard + of skType: + result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + else: + c.p.bracketExpr = n.sons[0] else: c.p.bracketExpr = n.sons[0] @@ -1287,7 +1365,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = n.sons[1] = takeImplicitAddr(c, ri) x.typ.flags.incl tfVarIsPtr -template resultTypeIsInferrable(typ: PType): expr = +template resultTypeIsInferrable(typ: PType): untyped = typ.isMetaType and typ.kind != tyTypeDesc proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = @@ -1313,15 +1391,16 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # --> `[]=`(a, i, x) let oldBracketExpr = c.p.bracketExpr a = semSubscript(c, a, {efLValue}) - if a == nil and mode != noOverloadedSubscript: + if a == nil: result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") add(result, n[1]) - result = semExprNoType(c, result) - c.p.bracketExpr = oldBracketExpr - return result - elif a == nil: - localError(n.info, "could not resolve: " & $n[0]) - return n + if mode == noOverloadedSubscript: + bracketNotFoundError(c, result) + return n + else: + result = semExprNoType(c, result) + c.p.bracketExpr = oldBracketExpr + return result c.p.bracketExpr = oldBracketExpr of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) @@ -1557,8 +1636,8 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo, result.flags = {sfGenSym} proc semExpandToAst(c: PContext, n: PNode): PNode = - var macroCall = n[1] - var expandedSym = expectMacroOrTemplateCall(c, macroCall) + let macroCall = n[1] + let expandedSym = expectMacroOrTemplateCall(c, macroCall) if expandedSym.kind == skError: return n macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) @@ -1566,11 +1645,12 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = styleCheckUse(n.info, expandedSym) for i in countup(1, macroCall.len-1): + #if macroCall.sons[0].typ.sons[i].kind != tyExpr: macroCall.sons[i] = semExprWithType(c, macroCall[i], {}) # Preserve the magic symbol in order to be handled in evals.nim internalAssert n.sons[0].sym.magic == mExpandToAst - #n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType + #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode" else: sysTypeFromName"PNimrodNode" result = n @@ -1810,8 +1890,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # the correct branch. Otherwise the AST will be passed through semStmt. result = nil - template setResult(e: expr) = - if semCheck: result = semStmt(c, e) # do not open a new scope! + template setResult(e: untyped) = + if semCheck: result = semExpr(c, e) # do not open a new scope! else: result = e # Check if the node is "when nimvm" @@ -1820,6 +1900,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # else: # ... var whenNimvm = false + var typ = commonTypeBegin if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and n.sons[1].kind == nkElse: let exprNode = n.sons[0].sons[0] @@ -1836,7 +1917,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = checkSonsLen(it, 2) if whenNimvm: if semCheck: - it.sons[1] = semStmt(c, it.sons[1]) + it.sons[1] = semExpr(c, it.sons[1]) + typ = commonType(typ, it.sons[1].typ) result = n # when nimvm is not elimited until codegen else: var e = semConstExpr(c, it.sons[0]) @@ -1850,12 +1932,14 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = checkSonsLen(it, 1) if result == nil or whenNimvm: if semCheck: - it.sons[0] = semStmt(c, it.sons[0]) + it.sons[0] = semExpr(c, it.sons[0]) + typ = commonType(typ, it.sons[0].typ) if result == nil: result = it.sons[0] else: illFormedAst(n) if result == nil: result = newNodeI(nkEmpty, n.info) + if whenNimvm: result.typ = typ # The ``when`` statement implements the mechanism for platform dependent # code. Thus we try to ensure here consistent ID allocation after the # ``when`` statement. @@ -2132,7 +2216,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: - var s = lookUp(c, n) + 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) result = semSym(c, n, s, flags) if s.kind in {skProc, skMethod, skConverter, skIterator}: @@ -2220,7 +2306,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = elif n.len == 1: result = semObjConstr(c, n, flags) elif contains(c.ambiguousSymbols, s.id): - localError(n.info, errUseQualifier, s.name.s) + errorUseQualifier(c, n.info, s) elif s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) of skProc, skMethod, skConverter, skIterator: @@ -2336,7 +2422,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "from") result = evalFrom(c, n) of nkIncludeStmt: - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include") + #if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include") result = evalInclude(c, n) of nkExportStmt, nkExportExceptStmt: if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "export") diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 5fe4e3299..02f238ae6 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -143,7 +143,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = const ordIntLit = {nkIntLit..nkUInt64Lit} result = n.typ - template commutativeOp(opr: expr) {.immediate.} = + template commutativeOp(opr: untyped) = let a = n.sons[1] let b = n.sons[2] if isIntRangeOrLit(a.typ) and isIntRangeOrLit(b.typ): @@ -151,7 +151,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = opr(pickMinInt(a), pickMinInt(b)), opr(pickMaxInt(a), pickMaxInt(b))) - template binaryOp(opr: expr) {.immediate.} = + template binaryOp(opr: untyped) = let a = n.sons[1] let b = n.sons[2] if isIntRange(a.typ) and b.kind in {nkIntLit..nkUInt64Lit}: @@ -286,13 +286,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mNot: result = newIntNodeT(1 - getInt(a), n) of mCard: result = newIntNodeT(nimsets.cardSet(a), n) of mBitnotI: result = newIntNodeT(not getInt(a), n) - of mLengthStr, mXLenStr: - if a.kind == nkNilLit: result = newIntNodeT(0, n) - else: result = newIntNodeT(len(getStr(a)), n) of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n) - of mLengthSeq, mLengthOpenArray, mXLenSeq: - if a.kind == nkNilLit: result = newIntNodeT(0, n) - else: result = newIntNodeT(sonsLen(a), n) # BUGFIX + of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr: + if a.kind == nkNilLit: + result = newIntNodeT(0, n) + elif a.kind in {nkStrLit..nkTripleStrLit}: + result = newIntNodeT(len a.strVal, n) + else: + result = newIntNodeT(sonsLen(a), n) # BUGFIX of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away of mToFloat, mToBiggestFloat: result = newFloatNodeT(toFloat(int(getInt(a))), n) @@ -419,7 +420,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)), int(getOrdValue(c))), n) of mFloatToStr: result = newStrNodeT($getFloat(a), n) - of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n) + of mCStrToStr, mCharToStr: + if a.kind == nkBracket: + var s = "" + for b in a.sons: + s.add b.getStrOrChar + result = newStrNodeT(s, n) + else: + result = newStrNodeT(getStrOrChar(a), n) of mStrToStr: result = a of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n) of mArrToSeq: @@ -482,7 +490,7 @@ proc leValueConv(a, b: PNode): bool = of nkCharLit..nkUInt64Lit: case b.kind of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal - of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal) + of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int else: internalError(a.info, "leValueConv") of nkFloatLit..nkFloat128Lit: case b.kind @@ -627,14 +635,20 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mCompileDate: result = newStrNodeT(times.getDateStr(), n) of mCompileTime: result = newStrNodeT(times.getClockStr(), n) of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n) - of mHostOS: result = newStrNodeT(toLower(platform.OS[targetOS].name), n) - of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLower, n) + of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n) + of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n) of mAppType: result = getAppType(n) of mNaN: result = newFloatNodeT(NaN, n) of mInf: result = newFloatNodeT(Inf, n) of mNegInf: result = newFloatNodeT(NegInf, n) + of mIntDefine: + if isDefined(s.name): + result = newIntNodeT(lookupSymbol(s.name).parseInt, n) + of mStrDefine: + if isDefined(s.name): + result = newStrNodeT(lookupSymbol(s.name), n) else: - if sfFakeConst notin s.flags: result = copyTree(s.ast) + result = copyTree(s.ast) of {skProc, skMethod}: result = n of skType: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 6651de78e..b8451865e 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -47,8 +47,11 @@ proc semGenericStmtScope(c: PContext, n: PNode, result = semGenericStmt(c, n, flags, ctx) closeScope(c) -template macroToExpand(s: expr): expr = - s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags) +template macroToExpand(s): untyped = + s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags) + +template macroToExpandSym(s): untyped = + s.kind in {skMacro, skTemplate} and (s.typ.len == 1) proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, ctx: var GenericCtx): PNode = @@ -61,14 +64,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skProc, skMethod, skIterator, skConverter, skModule: result = symChoice(c, n, s, scOpen) of skTemplate: - if macroToExpand(s): + if macroToExpandSym(s): styleCheckUse(n.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: result = symChoice(c, n, s, scOpen) of skMacro: - if macroToExpand(s): + if macroToExpandSym(s): styleCheckUse(n.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) @@ -124,7 +127,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, assert n.kind == nkDotExpr semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) - let luf = if withinMixin notin flags: {checkUndeclared} else: {} + let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule} var s = qualifiedLookUp(c, n, luf) if s != nil: @@ -199,24 +202,25 @@ proc semGenericStmt(c: PContext, n: PNode, if s != nil: incl(s.flags, sfUsed) mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles} - let scOption = if s.name.id in ctx.toMixin: scForceOpen else: scOpen + let sc = symChoice(c, fn, s, + if s.name.id in ctx.toMixin: scForceOpen else: scOpen) case s.kind of skMacro: - if macroToExpand(s): + if macroToExpand(s) and sc.safeLen <= 1: styleCheckUse(fn.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) - result = semGenericStmt(c, result, {}, ctx) + result = semGenericStmt(c, result, flags, ctx) else: - n.sons[0] = symChoice(c, fn, s, scOption) + n.sons[0] = sc result = n mixinContext = true of skTemplate: - if macroToExpand(s): + if macroToExpand(s) and sc.safeLen <= 1: styleCheckUse(fn.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) - result = semGenericStmt(c, result, {}, ctx) + result = semGenericStmt(c, result, flags, ctx) else: - n.sons[0] = symChoice(c, fn, s, scOption) + n.sons[0] = sc result = n # BUGFIX: we must not return here, we need to do first phase of # symbol lookup. Also since templates and macros can do scope injections @@ -227,7 +231,7 @@ proc semGenericStmt(c: PContext, n: PNode, # Leave it as an identifier. discard of skProc, skMethod, skIterator, skConverter, skModule: - result.sons[0] = symChoice(c, fn, s, scOption) + result.sons[0] = sc # do not check of 's.magic==mRoof' here because it might be some # other '^' but after overload resolution the proper one: if ctx.bracketExpr != nil and n.len == 2 and s.name.s == "^": @@ -426,7 +430,12 @@ proc semGenericStmt(c: PContext, n: PNode, n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx) var body: PNode - if n.sons[namePos].kind == nkSym: body = n.sons[namePos].sym.getBody + if n.sons[namePos].kind == nkSym: + let s = n.sons[namePos].sym + if sfGenSym in s.flags and s.ast == nil: + body = n.sons[bodyPos] + else: + body = s.getBody else: body = n.sons[bodyPos] n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx) closeScope(c) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 4a45dee9d..460db4f7c 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -43,9 +43,11 @@ proc rawHandleSelf(c: PContext; owner: PSym) = if arg.name.id == c.selfName.id: c.p.selfSym = arg arg.flags.incl sfIsSelf - let t = c.p.selfSym.typ.skipTypes(abstractPtrs) - if t.kind == tyObject: + var t = c.p.selfSym.typ.skipTypes(abstractPtrs) + while t.kind == tyObject: addObjFieldsToLocalScope(c, t.n) + if t.sons[0] == nil: break + t = t.sons[0].skipTypes(abstractPtrs) proc pushProcCon*(c: PContext; owner: PSym) = rawPushProcCon(c, owner) @@ -111,9 +113,9 @@ proc removeDefaultParamValues(n: PNode) = # not possible... XXX We don't solve this issue here. a.sons[L-1] = ast.emptyNode -proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = +proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) = # we need to create a fresh set of gensym'ed symbols: - if n.kind == nkSym and sfGenSym in n.sym.flags: + if n.kind == nkSym and sfGenSym in n.sym.flags and n.sym.owner == orig: let s = n.sym var x = PSym(idTableGet(symMap, s)) if x == nil: @@ -122,7 +124,7 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = idTablePut(symMap, s, x) n.sym = x else: - for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap) + for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, orig, symMap) proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) @@ -137,7 +139,7 @@ proc addProcDecls(c: PContext, fn: PSym) = maybeAddResult(c, fn, fn.ast) -proc instantiateBody(c: PContext, n, params: PNode, result: PSym) = +proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = if n.sons[bodyPos].kind != nkEmpty: inc c.inGenericInst # add it here, so that recursive generic procs are possible: @@ -149,7 +151,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result: PSym) = let param = params[i].sym if sfGenSym in param.flags: idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym) - freshGenSyms(b, result, symMap) + freshGenSyms(b, result, orig, symMap) b = semProcBody(c, b) b = hloBody(c, b) n.sons[bodyPos] = transformBody(c.module, b, result) @@ -165,7 +167,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) = openScope(c) var n = oldPrc.ast n.sons[bodyPos] = copyTree(s.getBody) - instantiateBody(c, n, nil, oldPrc) + instantiateBody(c, n, nil, oldPrc, s) closeScope(c) popInfoContext() @@ -203,7 +205,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # The solution would be to move this logic into semtypinst, but # at this point semtypinst have to become part of sem, because it # will need to use openScope, addDecl, etc. - addDecl(c, prc) + #addDecl(c, prc) pushInfoContext(info) var cl = initTypeVars(c, pt, info, nil) @@ -312,7 +314,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) if isNil(n.sons[bodyPos]): n.sons[bodyPos] = copyTree(fn.getBody) - instantiateBody(c, n, fn.typ.n, result) + instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) paramsTypeCheck(c, result.typ) else: diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index 150680af7..6cd5c4a3c 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -12,25 +12,26 @@ import ast, astalgo, msgs, types -proc ithField(n: PNode, field: int): PSym = +proc ithField(n: PNode, field: var int): PSym = result = nil case n.kind of nkRecList: for i in countup(0, sonsLen(n) - 1): - result = ithField(n.sons[i], field-i) + result = ithField(n.sons[i], field) if result != nil: return of nkRecCase: if n.sons[0].kind != nkSym: internalError(n.info, "ithField") - result = ithField(n.sons[0], field-1) + result = ithField(n.sons[0], field) if result != nil: return for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkOfBranch, nkElse: - result = ithField(lastSon(n.sons[i]), field-1) + result = ithField(lastSon(n.sons[i]), field) if result != nil: return else: internalError(n.info, "ithField(record case branch)") of nkSym: if field == 0: result = n.sym + else: dec(field) else: discard proc annotateType*(n: PNode, t: PType) = @@ -39,10 +40,13 @@ proc annotateType*(n: PNode, t: PType) = # to not to skip tyGenericInst case n.kind of nkObjConstr: + let x = t.skipTypes(abstractPtrs) n.typ = t for i in 1 .. <n.len: - let field = x.n.ithField(i - 1) - if field.isNil: globalError n.info, "invalid field at index " & $i + var j = i-1 + let field = x.n.ithField(j) + if field.isNil: + globalError n.info, "invalid field at index " & $i else: internalAssert(n.sons[i].kind == nkExprColonExpr) annotateType(n.sons[i].sons[1], field.typ) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 1a70e4a12..cbe9bc176 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -42,7 +42,10 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = result = semSubscript(c, result, flags) c.p.bracketExpr = oldBracketExpr if result.isNil: - localError(n.info, "could not resolve: " & $n) + let x = copyTree(n) + x.sons[0] = newIdentNode(getIdent"[]", n.info) + bracketNotFoundError(c, x) + #localError(n.info, "could not resolve: " & $n) result = n proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = @@ -111,8 +114,12 @@ proc semTypeTraits(c: PContext, n: PNode): PNode = proc semOrd(c: PContext, n: PNode): PNode = result = n - result.typ = makeRangeType(c, firstOrd(n.sons[1].typ), - lastOrd(n.sons[1].typ), n.info) + let parType = n.sons[1].typ + if isOrdinalType(parType) or parType.kind == tySet: + result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info) + else: + localError(n.info, errOrdinalTypeExpected) + result.typ = errorType(c) proc semBindSym(c: PContext, n: PNode): PNode = result = copyNode(n) @@ -130,7 +137,7 @@ proc semBindSym(c: PContext, n: PNode): PNode = return errorNode(c, n) let id = newIdentNode(getIdent(sl.strVal), n.info) - let s = qualifiedLookUp(c, id) + let s = qualifiedLookUp(c, id, {checkUndeclared}) if s != nil: # we need to mark all symbols: var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal)) @@ -203,7 +210,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = n.sons[1] else: result = newNodeIT(nkCall, n.info, getSysType(tyInt)) - result.add newSymNode(getSysMagic("-", mSubI), n.info) + let subi = getSysMagic("-", mSubI) + #echo "got ", typeToString(subi.typ) + result.add newSymNode(subi, n.info) result.add lenExprB result.add n.sons[1] of mPlugin: diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index b04ba4657..90c1a315a 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -74,8 +74,6 @@ type currentSpawnId: int inLoop: int -let opSlice = createMagic("slice", mSlice) - proc initAnalysisCtx(): AnalysisCtx = result.locals = @[] result.slices = @[] @@ -123,7 +121,7 @@ proc checkLocal(c: AnalysisCtx; n: PNode) = else: for i in 0 .. <n.safeLen: checkLocal(c, n.sons[i]) -template `?`(x): expr = x.renderTree +template `?`(x): untyped = x.renderTree proc checkLe(c: AnalysisCtx; a, b: PNode) = case proveLe(c.guards, a, b) @@ -262,7 +260,7 @@ proc min(a, b: PNode): PNode = proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags -template pushSpawnId(c: expr, body: stmt) {.immediate, dirty.} = +template pushSpawnId(c, body) {.dirty.} = inc c.spawns let oldSpawnId = c.currentSpawnId c.currentSpawnId = c.spawns @@ -399,7 +397,9 @@ proc transformSlices(n: PNode): PNode = let op = n[0].sym if op.name.s == "[]" and op.fromSystem: result = copyNode(n) - result.add opSlice.newSymNode + let opSlice = newSymNode(createMagic("slice", mSlice)) + opSlice.typ = getSysType(tyInt) + result.add opSlice result.add n[1] let slice = n[2].skipStmtList result.add slice[1] diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index c3a9e01a0..b12ab5e96 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -151,7 +151,7 @@ proc guardDotAccess(a: PEffects; n: PNode) = guardGlobal(a, n, g) proc makeVolatile(a: PEffects; s: PSym) {.inline.} = - template compileToCpp(a): expr = + template compileToCpp(a): untyped = gCmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags if a.inTryStmt > 0 and not compileToCpp(a): incl(s.flags, sfVolatile) @@ -459,7 +459,7 @@ proc documentRaises*(n: PNode) = if p4 != nil: n.sons[pragmasPos].add p4 if p5 != nil: n.sons[pragmasPos].add p5 -template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {} +template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {} proc importedFromC(n: PNode): bool = # when imported from C, we assume GC-safety. @@ -505,7 +505,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = # addr(x[]) can't be proven, but addr(x) can: if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return elif (n.kind == nkSym and n.sym.kind in routineKinds) or - n.kind in procDefs+{nkObjConstr}: + n.kind in procDefs+{nkObjConstr, nkBracket}: # 'p' is not nil obviously: return case impliesNotNil(tracked.guards, n) @@ -532,7 +532,7 @@ proc isOwnedProcVar(n: PNode; owner: PSym): bool = proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = let a = skipConvAndClosure(n) let op = a.typ - if op != nil and op.kind == tyProc and n.kind != nkNilLit: + if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit: internalAssert op.n.sons[0].kind == nkEffectList var effectList = op.n.sons[0] let s = n.skipConv diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 910267e57..5f9989ea2 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -17,7 +17,8 @@ proc semDiscard(c: PContext, n: PNode): PNode = checkSonsLen(n, 1) if n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) - if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard) + if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone: + localError(n.info, errInvalidDiscard) proc semBreakOrContinue(c: PContext, n: PNode): PNode = result = n @@ -137,7 +138,7 @@ proc fixNilType(n: PNode) = proc discardCheck(c: PContext, result: PNode) = if c.inTypeClass > 0: return - if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: + if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if result.kind == nkNilLit: result.typ = nil message(result.info, warnNilStatement) @@ -303,8 +304,14 @@ proc semTry(c: PContext, n: PNode): PNode = proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result = fitNode(c, typ, n) if result.kind in {nkHiddenStdConv, nkHiddenSubConv}: - changeType(result.sons[1], typ, check=true) - result = result.sons[1] + let r1 = result.sons[1] + if r1.kind in {nkCharLit..nkUInt64Lit} and typ.skipTypes(abstractRange).kind in {tyFloat..tyFloat128}: + result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal) + result.info = n.info + result.typ = typ + else: + changeType(r1, typ, check=true) + result = r1 elif not sameType(result.typ, typ): changeType(result, typ, check=false) @@ -336,7 +343,7 @@ proc checkNilable(v: PSym) = {tfNotNil, tfNeedsInit} * v.typ.flags != {}: if v.ast.isNil: message(v.info, warnProveInit, v.name.s) - elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: + elif tfNeedsInit in v.typ.flags and tfNotNil notin v.ast.typ.flags: message(v.info, warnProveInit, v.name.s) include semasgn @@ -410,6 +417,13 @@ proc semUsing(c: PContext; n: PNode): PNode = if a.sons[length-1].kind != nkEmpty: localError(a.info, "'using' sections cannot contain assignments") +proc hasEmpty(typ: PType): bool = + if typ.kind in {tySequence, tyArray, tySet}: + result = typ.lastSon.kind == tyEmpty + elif typ.kind == tyTuple: + for s in typ.sons: + result = result or hasEmpty(s) + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) @@ -444,10 +458,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = #changeType(def.skipConv, typ, check=true) else: typ = skipIntLit(def.typ) - if typ.kind in {tySequence, tyArray, tySet} and - typ.lastSon.kind == tyEmpty: + if hasEmpty(typ): localError(def.info, errCannotInferTypeOfTheLiteral, - ($typ.kind).substr(2).toLower) + ($typ.kind).substr(2).toLowerAscii) else: def = ast.emptyNode if symkind == skLet: localError(a.info, errLetNeedsInit) @@ -535,7 +548,7 @@ proc semConst(c: PContext, n: PNode): PNode = localError(a.sons[2].info, errConstExprExpected) continue if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: - localError(a.info, errXisNoType, typeToString(typ)) + localError(a.info, "invalid type for const: " & typeToString(typ)) continue v.typ = typ v.ast = def # no need to copy @@ -668,7 +681,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = let name = a.sons[0] var s: PSym if name.kind == nkDotExpr: - s = qualifiedLookUp(c, name) + 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: localError(name.info, "only .partial objects can be extended") else: @@ -710,7 +723,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = a.sons[1] = s.typ.n s.typ.size = -1 # could not be computed properly # we fill it out later. For magic generics like 'seq', it won't be filled - # so we use tyEmpty instead of nil to not crash for strange conversions + # so we use tyNone instead of nil to not crash for strange conversions # like: mydata.seq rawAddSon(s.typ, newTypeS(tyNone, c)) s.ast = a @@ -778,11 +791,16 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = var s = a.sons[0].sym # compute the type's size and check for illegal recursions: if a.sons[1].kind == nkEmpty: - if a.sons[2].kind in {nkSym, nkIdent, nkAccQuoted}: + var x = a[2] + while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0: + x = x.lastSon + if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and + s.typ.kind notin {tyObject, tyEnum}: # type aliases are hard: - #MessageOut('for type ' + typeToString(s.typ)); - var t = semTypeNode(c, a.sons[2], nil) - if t.kind in {tyObject, tyEnum}: + var t = semTypeNode(c, x, nil) + assert t != nil + if t.kind in {tyObject, tyEnum, tyDistinct}: + assert s.typ != nil assignType(s.typ, t) s.typ.id = t.id # same id checkConstructedType(s.info, s.typ) @@ -924,6 +942,17 @@ proc semProcAnnotation(c: PContext, prc: PNode; pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) return +proc setGenericParamsMisc(c: PContext; n: PNode): PNode = + let orig = n.sons[genericParamsPos] + # we keep the original params around for better error messages, see + # issue https://github.com/nim-lang/Nim/issues/1713 + result = semGenericParamList(c, orig) + if n.sons[miscPos].kind == nkEmpty: + n.sons[miscPos] = newTree(nkBracket, ast.emptyNode, orig) + else: + n.sons[miscPos].sons[1] = orig + n.sons[genericParamsPos] = result + proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = # XXX semProcAux should be good enough for this now, we will eventually # remove semLambda @@ -942,8 +971,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = openScope(c) var gp: PNode if n.sons[genericParamsPos].kind != nkEmpty: - n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) - gp = n.sons[genericParamsPos] + gp = setGenericParamsMisc(c, n) else: gp = newNodeI(nkGenericParams, n.info) @@ -1165,8 +1193,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, openScope(c) var gp: PNode if n.sons[genericParamsPos].kind != nkEmpty: - n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) - gp = n.sons[genericParamsPos] + gp = setGenericParamsMisc(c, n) else: gp = newNodeI(nkGenericParams, n.info) # process parameters: @@ -1231,6 +1258,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, pushOwner(s) s.options = gOptions if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) + if s.name.s[0] in {'.', '('}: + if s.name.s in [".", ".()", ".=", "()"] and not experimentalMode(c): + message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s) if n.sons[bodyPos].kind != nkEmpty: # for DLL generation it is annoying to check for sfImportc! if sfBorrow in s.flags: @@ -1245,7 +1275,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, c.p.wasForwarded = proto != nil maybeAddResult(c, s, n) - if sfImportc notin s.flags: + if lfDynamicLib notin s.loc.flags: # no semantic checking for importc: let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) # unfortunately we cannot skip this step when in 'system.compiles' @@ -1285,6 +1315,7 @@ proc determineType(c: PContext, s: PSym) = proc semIterator(c: PContext, n: PNode): PNode = # gensym'ed iterator? + let isAnon = n[namePos].kind == nkEmpty if n[namePos].kind == nkSym: # gensym'ed iterators might need to become closure iterators: n[namePos].sym.owner = getCurrOwner() @@ -1294,6 +1325,8 @@ proc semIterator(c: PContext, n: PNode): PNode = var t = s.typ if t.sons[0] == nil and s.typ.callConv != ccClosure: localError(n.info, errXNeedsReturnType, "iterator") + if isAnon and s.typ.callConv == ccInline: + localError(n.info, "inline iterators are not first-class / cannot be assigned to variables") # iterators are either 'inline' or 'closure'; for backwards compatibility, # we require first class iterators to be marked with 'closure' explicitly # -- at least for 0.9.2. @@ -1325,9 +1358,24 @@ proc finishMethod(c: PContext, s: PSym) = proc semMethod(c: PContext, n: PNode): PNode = if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method") result = semProcAux(c, n, skMethod, methodPragmas) - + # macros can transform methods to nothing: + if namePos >= result.safeLen: return result var s = result.sons[namePos].sym - if not isGenericRoutine(s): + if isGenericRoutine(s): + let tt = s.typ + var foundObj = false + # we start at 1 for now so that tparsecombnum continues to compile. + # XXX Revisit this problem later. + for col in countup(1, sonsLen(tt)-1): + let t = tt.sons[col] + if t != nil and t.kind == tyGenericInvocation: + var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst, tyGenericInvocation, tyGenericBody}) + if x.kind == tyObject and t.len-1 == result.sons[genericParamsPos].len: + foundObj = true + x.methods.safeAdd((col,s)) + if not foundObj: + message(n.info, warnDeprecated, "generic method not attachable to object type") + else: # why check for the body? bug #2400 has none. Checking for sfForward makes # no sense either. # and result.sons[bodyPos].kind != nkEmpty: @@ -1340,6 +1388,8 @@ proc semConverterDef(c: PContext, n: PNode): PNode = if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter") checkSonsLen(n, bodyPos + 1) result = semProcAux(c, n, skConverter, converterPragmas) + # macros can transform converters to nothing: + if namePos >= result.safeLen: return result var s = result.sons[namePos].sym var t = s.typ if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "converter") @@ -1349,6 +1399,8 @@ proc semConverterDef(c: PContext, n: PNode): PNode = proc semMacroDef(c: PContext, n: PNode): PNode = checkSonsLen(n, bodyPos + 1) result = semProcAux(c, n, skMacro, macroPragmas) + # macros can transform macros to nothing: + if namePos >= result.safeLen: return result var s = result.sons[namePos].sym var t = s.typ if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro") diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a4498a3ae..20b5071ac 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -51,9 +51,10 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = var i = 0 a = initOverloadIter(o, c, n) while a != nil: + if a.kind != skModule: + inc(i) + if i > 1: break a = nextOverloadIter(o, c, n) - inc(i) - if i > 1: break if i <= 1 and r != scForceOpen: # XXX this makes more sense but breaks bootstrapping for now: # (s.kind notin routineKinds or s.magic != mNone): @@ -68,8 +69,9 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = result = newNodeIT(kind, n.info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: - incl(a.flags, sfUsed) - addSon(result, newSymNode(a, n.info)) + if a.kind != skModule: + incl(a.flags, sfUsed) + addSon(result, newSymNode(a, n.info)) a = nextOverloadIter(o, c, n) proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = @@ -80,7 +82,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = # the same symbol! # This is however not true anymore for hygienic templates as semantic # processing for them changes the symbol table... - let s = qualifiedLookUp(c, a) + let s = qualifiedLookUp(c, a, {checkUndeclared}) if s != nil: # we need to mark all symbols: let sc = symChoice(c, n, s, scClosed) @@ -577,13 +579,16 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = else: gp = newNodeI(nkGenericParams, n.info) # process parameters: + var allUntyped = true if n.sons[paramsPos].kind != nkEmpty: semParamList(c, n.sons[paramsPos], gp, s) # a template's parameters are not gensym'ed even if that was originally the # case as we determine whether it's a template parameter in the template # body by the absence of the sfGenSym flag: for i in 1 .. s.typ.n.len-1: - s.typ.n.sons[i].sym.flags.excl sfGenSym + let param = s.typ.n.sons[i].sym + param.flags.excl sfGenSym + if param.typ.kind != tyExpr: allUntyped = false if sonsLen(gp) > 0: if n.sons[genericParamsPos].kind == nkEmpty: # we have a list of implicit type parameters: @@ -599,6 +604,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = s.typ.n = newNodeI(nkFormalParams, n.info) rawAddSon(s.typ, newTypeS(tyStmt, c)) addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0])) + if allUntyped: incl(s.flags, sfAllUntyped) if n.sons[patternPos].kind != nkEmpty: n.sons[patternPos] = semPattern(c, n.sons[patternPos]) var ctx: TemplCtx @@ -628,8 +634,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = c.patterns.add(s) proc semPatternBody(c: var TemplCtx, n: PNode): PNode = - template templToExpand(s: expr): expr = - s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags) + template templToExpand(s: untyped): untyped = + s.kind == skTemplate and (s.typ.len == 1 or sfAllUntyped in s.flags) proc newParam(c: var TemplCtx, n: PNode, s: PSym): PNode = # the param added in the current scope is actually wrong here for diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index ba17cc307..9d00c06ca 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -130,7 +130,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = if n.len < 1: result = newConstraint(c, kind) else: - let isCall = ord(n.kind in nkCallKinds) + let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr}) let n = if n[0].kind == nkBracket: n[0] else: n checkMinSonsLen(n, 1) var base = semTypeNode(c, n.lastSon, nil) @@ -370,7 +370,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = result.n = newNodeI(nkRecList, n.info) var check = initIntSet() var counter = 0 - for i in countup(0, sonsLen(n) - 1): + for i in countup(ord(n.kind == nkBracketExpr), sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) @@ -393,20 +393,24 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field) + if result.n.len == 0: result.n = nil proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym = # identifier with visibility if n.kind == nkPostfix: - if sonsLen(n) == 2 and n.sons[0].kind == nkIdent: + if sonsLen(n) == 2: # for gensym'ed identifiers the identifier may already have been # transformed to a symbol and we need to use that here: result = newSymG(kind, n.sons[1], c) - var v = n.sons[0].ident + var v = considerQuotedIdent(n.sons[0]) if sfExported in allowed and v.id == ord(wStar): incl(result.flags, sfExported) else: - localError(n.sons[0].info, errInvalidVisibilityX, v.s) + if not (sfExported in allowed): + localError(n.sons[0].info, errXOnlyAtModuleScope, "export") + else: + localError(n.sons[0].info, errInvalidVisibilityX, renderTree(n[0])) else: illFormedAst(n) else: @@ -751,18 +755,18 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, addDecl(c, s) # XXX: There are codegen errors if this is turned into a nested proc - template liftingWalk(typ: PType, anonFlag = false): expr = + template liftingWalk(typ: PType, anonFlag = false): untyped = liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) #proc liftingWalk(paramType: PType, anon = false): PType = var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name else: nil - template maybeLift(typ: PType): expr = + template maybeLift(typ: PType): untyped = let lifted = liftingWalk(typ) (if lifted != nil: lifted else: typ) - template addImplicitGeneric(e: expr): expr = + template addImplicitGeneric(e): untyped = addImplicitGenericImpl(e, paramTypId) case paramType.kind: @@ -942,7 +946,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if isType: localError(a.info, "':' expected") if kind in {skTemplate, skMacro}: typ = newTypeS(tyExpr, c) - elif skipTypes(typ, {tyGenericInst}).kind == tyEmpty: + elif skipTypes(typ, {tyGenericInst}).kind == tyVoid: continue for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) @@ -974,7 +978,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if r != nil: # turn explicit 'void' return type into 'nil' because the rest of the # compiler only checks for 'nil': - if skipTypes(r, {tyGenericInst}).kind != tyEmpty: + if skipTypes(r, {tyGenericInst}).kind != tyVoid: # 'auto' as a return type does not imply a generic: if r.kind == tyAnything: # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the @@ -1035,7 +1039,7 @@ proc semGenericParamInInvocation(c: PContext, n: PNode): PType = proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(n.info, "cannot instantiate the '$1' $2" % - [s.name.s, ($s.kind).substr(2).toLower]) + [s.name.s, ($s.kind).substr(2).toLowerAscii]) return newOrPrevType(tyError, prev, c) var t = s.typ @@ -1094,10 +1098,14 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = result = instGenericContainer(c, n.info, result, allowMetaTypes = false) -proc semTypeExpr(c: PContext, n: PNode): PType = +proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = var n = semExprWithType(c, n, {efDetermineType}) if n.typ.kind == tyTypeDesc: result = n.typ.base + # fix types constructed by macros: + if prev != nil and prev.sym != nil and result.sym.isNil: + result.sym = prev.sym + result.sym.typ = result else: localError(n.info, errTypeExpected, n.renderTree) result = errorType(c) @@ -1177,7 +1185,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: localError(n.info, errGenerated, "invalid type") elif n[0].kind notin nkIdentKinds: - result = semTypeExpr(c, n) + result = semTypeExpr(c, n, prev) else: let op = considerQuotedIdent(n.sons[0]) if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": @@ -1218,7 +1226,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = let typExpr = semExprWithType(c, n.sons[1], {efInTypeof}) result = typExpr.typ else: - result = semTypeExpr(c, n) + result = semTypeExpr(c, n, prev) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType @@ -1241,6 +1249,19 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = copyType(result, getCurrOwner(), false) for i in countup(1, n.len - 1): result.rawAddSon(semTypeNode(c, n.sons[i], nil)) + of mDistinct: + result = newOrPrevType(tyDistinct, prev, c) + addSonSkipIntLit(result, semTypeNode(c, n[1], nil)) + of mVar: + result = newOrPrevType(tyVar, prev, c) + var base = semTypeNode(c, n.sons[1], nil) + if base.kind == tyVar: + localError(n.info, errVarVarTypeNotAllowed) + base = base.sons[0] + addSonSkipIntLit(result, base) + of mRef: result = semAnyRef(c, n, tyRef, prev) + of mPtr: result = semAnyRef(c, n, tyPtr, prev) + of mTuple: result = semTuple(c, n, prev) else: result = semGeneric(c, n, s, prev) of nkDotExpr: var typeExpr = semExpr(c, n) @@ -1370,10 +1391,7 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: - setMagicType(m, tyEmpty, 0) - # for historical reasons we conflate 'void' with 'empty' so that '@[]' - # has the type 'seq[void]'. - m.typ.flags.incl tfVoid + setMagicType(m, tyVoid, 0) of mArray: setMagicType(m, tyArray, 0) of mOpenArray: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 7ff33f918..12620d55d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -162,7 +162,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = discard of nkSym: result.sym = replaceTypeVarsS(cl, n.sym) - if result.sym.typ.kind == tyEmpty: + if result.sym.typ.kind == tyVoid: # don't add the 'void' field result = newNode(nkRecList, n.info) of nkRecWhen: @@ -289,7 +289,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # but we already raised an error! rawAddSon(result, header.sons[i]) - var newbody = replaceTypeVarsT(cl, lastSon(body)) + let bbody = lastSon body + var newbody = replaceTypeVarsT(cl, bbody) cl.skipTypedesc = oldSkipTypedesc newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) result.flags = result.flags + newbody.flags - tfInstClearedFlags @@ -306,25 +307,31 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # 'deepCopy' needs to be instantiated for # generics *when the type is constructed*: newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, - attachedDeepCopy) + attachedDeepCopy, 1) let asgn = newbody.assignment if asgn != nil and sfFromGeneric notin asgn.flags: # '=' needs to be instantiated for generics when the type is constructed: newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info, - attachedAsgn) + attachedAsgn, 1) + let methods = skipTypes(bbody, abstractPtrs).methods + for col, meth in items(methods): + # we instantiate the known methods belonging to that type, this causes + # them to be registered and that's enough, so we 'discard' the result. + discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info, + attachedAsgn, col) proc eraseVoidParams*(t: PType) = # transform '(): void' into '()' because old parts of the compiler really # don't deal with '(): void': - if t.sons[0] != nil and t.sons[0].kind == tyEmpty: + if t.sons[0] != nil and t.sons[0].kind == tyVoid: t.sons[0] = nil for i in 1 .. <t.sonsLen: # don't touch any memory unless necessary - if t.sons[i].kind == tyEmpty: + if t.sons[i].kind == tyVoid: var pos = i for j in i+1 .. <t.sonsLen: - if t.sons[j].kind != tyEmpty: + if t.sons[j].kind != tyVoid: t.sons[pos] = t.sons[j] t.n.sons[pos] = t.n.sons[j] inc pos @@ -504,6 +511,6 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, popInfoContext() template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, - t: PType): expr = + t: PType): untyped = generateTypeInstance(p, pt, arg.info, t) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 82f878932..ec429968c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -22,7 +22,7 @@ type TCandidateState* = enum csEmpty, csMatch, csNoMatch - CandidateErrors* = seq[PSym] + CandidateErrors* = seq[(PSym,int)] TCandidate* = object c*: PContext exactMatches*: int # also misused to prefer iters over procs @@ -49,6 +49,7 @@ type # a distrinct type typedescMatched*: bool isNoCall*: bool # misused for generic type instantiations C[T] + mutabilityProblem*: uint8 # tyVar mismatch inheritancePenalty: int # to prefer closest father object type errors*: CandidateErrors # additional clarifications to be displayed to the # user if overload resolution fails @@ -96,8 +97,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) = c.calleeSym = nil initIdTable(c.bindings) -proc put(t: var TIdTable, key, val: PType) {.inline.} = - idTablePut(t, key, val.skipIntLit) +proc put(c: var TCandidate, key, val: PType) {.inline.} = + idTablePut(c.bindings, key, val.skipIntLit) proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, binding: PNode, calleeScope = -1) = @@ -129,7 +130,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, bound = makeTypeDesc(ctx, bound) else: bound = bound.skipTypes({tyTypeDesc}) - put(c.bindings, formalTypeParam, bound) + put(c, formalTypeParam, bound) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = @@ -220,13 +221,14 @@ proc cmpCandidates*(a, b: TCandidate): int = if result != 0: return result = a.convMatches - b.convMatches if result != 0: return - result = a.calleeScope - b.calleeScope - if result != 0: return # the other way round because of other semantics: result = b.inheritancePenalty - a.inheritancePenalty if result != 0: return # prefer more specialized generic over more general generic: result = complexDisambiguation(a.callee, b.callee) + # only as a last resort, consider scoping: + if result != 0: return + result = a.calleeScope - b.calleeScope proc writeMatches*(c: TCandidate) = writeLine(stdout, "exact matches: " & $c.exactMatches) @@ -242,6 +244,8 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = for i in 1 .. <arg.len: result.add(" | ") result.add typeToString(arg[i].typ, prefer) + elif arg.typ == nil: + result = "void" else: result = arg.typ.typeToString(prefer) @@ -253,15 +257,16 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; if n.sons[i].kind == nkExprEqExpr: add(result, renderTree(n.sons[i].sons[0])) add(result, ": ") - if arg.typ.isNil: + if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}: + # XXX we really need to 'tryExpr' here! arg = c.semOperand(c, n.sons[i].sons[1]) n.sons[i].typ = arg.typ n.sons[i].sons[1] = arg else: - if arg.typ.isNil: + if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}: arg = c.semOperand(c, n.sons[i]) n.sons[i] = arg - if arg.typ.kind == tyError: return + if arg.typ != nil and arg.typ.kind == tyError: return add(result, argTypeToString(arg, prefer)) if i != sonsLen(n) - 1: add(result, ", ") @@ -360,6 +365,50 @@ proc isObjectSubtype(a, f: PType): int = if t != nil: result = depth +type + SkippedPtr = enum skippedNone, skippedRef, skippedPtr + +proc skipToObject(t: PType; skipped: var SkippedPtr): PType = + var r = t + # we're allowed to skip one level of ptr/ref: + var ptrs = 0 + while r != nil: + case r.kind + of tyGenericInvocation: + r = r.sons[0] + of tyRef: + inc ptrs + skipped = skippedRef + r = r.lastSon + of tyPtr: + inc ptrs + skipped = skippedPtr + r = r.lastSon + of tyGenericBody, tyGenericInst: + r = r.lastSon + else: + break + if r.kind == tyObject and ptrs <= 1: result = r + +proc isGenericSubtype(a, f: PType, d: var int): bool = + assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} + var askip = skippedNone + var fskip = skippedNone + var t = a.skipToObject(askip) + let r = f.skipToObject(fskip) + if r == nil: return false + var depth = 0 + # XXX sameObjectType can return false here. Need to investigate + # why that is but sameObjectType does way too much work here anyway. + while t != nil and r.sym != t.sym and askip == fskip: + t = t.sons[0] + if t != nil: t = t.skipToObject(askip) + else: break + inc depth + if t != nil and askip == fskip: + d = depth + result = true + proc minRel(a, b: TTypeRelation): TTypeRelation = if a <= b: result = a else: result = b @@ -513,7 +562,7 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = proc matchUserTypeClass*(c: PContext, m: var TCandidate, ff, a: PType): TTypeRelation = var body = ff.skipTypes({tyUserTypeClassInst}) - if c.inTypeClass > 20: + if c.inTypeClass > 4: localError(body.n[3].info, $body.n[3] & " too nested for type matching") return isNone @@ -531,7 +580,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, typ = ff.sons[i] param: PSym - template paramSym(kind): expr = + template paramSym(kind): untyped = newSym(kind, typeParamName, body.sym, body.sym.info) case typ.kind @@ -598,6 +647,10 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode = let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil) result = c.c.semExpr(c.c, instantiated) +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 = # typeRel can be used to establish various relationships between types: # @@ -619,7 +672,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = assert(f != nil) if f.kind == tyExpr: - if aOrig != nil: put(c.bindings, f, aOrig) + if aOrig != nil: put(c, f, aOrig) return isGeneric assert(aOrig != nil) @@ -640,10 +693,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = template bindingRet(res) = if doBind: let bound = aOrig.skipTypes({tyRange}).skipIntLit - if doBind: put(c.bindings, f, bound) + put(c, f, bound) return res - template considerPreviousT(body: stmt) {.immediate.} = + template considerPreviousT(body: untyped) = var prev = PType(idTableGet(c.bindings, f)) if prev == nil: body else: return typeRel(c, prev, a) @@ -737,6 +790,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyVar: if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) else: result = typeRel(c, f.base, aOrig) + subtypeCheck() of tyArray, tyArrayConstr: # tyArrayConstr cannot happen really, but # we wanna be safe here @@ -746,7 +800,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if fRange.kind == tyGenericParam: var prev = PType(idTableGet(c.bindings, fRange)) if prev == nil: - put(c.bindings, fRange, a.sons[0]) + put(c, fRange, a.sons[0]) fRange = a else: fRange = prev @@ -764,7 +818,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # we must correct for the off-by-one discrepancy between # ranges and static params: replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1) - put(c.bindings, rangeStaticT, replacementT) + put(c, rangeStaticT, replacementT) return isGeneric let len = tryResolvingStaticExpr(c, fRange.n[1]) @@ -851,6 +905,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if sameDistinctTypes(f, a): result = isEqual elif f.base.kind == tyAnything: result = isGeneric elif c.coerceDistincts: result = typeRel(c, f.base, a) + elif a.kind == tyNil and f.base.kind in NilableTypes: + result = f.allowsNil elif c.coerceDistincts: result = typeRel(c, f.base, a) of tySet: if a.kind == tySet: @@ -867,6 +923,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = 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) + subtypeCheck() if result <= isConvertible: result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: result = isNilConversion @@ -921,8 +978,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isConvertible else: discard - of tyEmpty: - if a.kind == tyEmpty: result = isEqual + of tyEmpty, tyVoid: + if a.kind == f.kind: result = isEqual of tyGenericInst: result = typeRel(c, lastSon(f), a) @@ -932,21 +989,26 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if a.kind == tyGenericInst and a.sons[0] == f: bindingRet isGeneric let ff = lastSon(f) - if ff != nil: result = typeRel(c, ff, a) + if ff != nil: + result = typeRel(c, ff, a) of tyGenericInvocation: var x = a.skipGenericAlias + var depth = 0 if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody: #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") # simply no match for now: discard elif x.kind == tyGenericInst and - (f.sons[0] == x.sons[0]) and + ((f.sons[0] == x.sons[0]) or isGenericSubtype(x, f, depth)) and (sonsLen(x) - 1 == sonsLen(f)): for i in countup(1, sonsLen(f) - 1): if x.sons[i].kind == tyGenericParam: internalError("wrong instantiated type!") - elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return + elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: + # Workaround for regression #4589 + if f.sons[i].kind != tyTypeDesc: return + c.inheritancePenalty += depth result = isGeneric else: let genericBody = f.sons[0] @@ -963,13 +1025,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # # we steal the generic parameters from the tyGenericBody: for i in countup(1, sonsLen(f) - 1): - var x = PType(idTableGet(c.bindings, genericBody.sons[i-1])) + let x = PType(idTableGet(c.bindings, genericBody.sons[i-1])) if x == nil: discard "maybe fine (for eg. a==tyNil)" elif x.kind in {tyGenericInvocation, tyGenericParam}: internalError("wrong instantiated type!") else: - put(c.bindings, f.sons[i], x) + put(c, f.sons[i], x) of tyAnd: considerPreviousT: @@ -979,6 +1041,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if x < isSubtype: return isNone # 'and' implies minimum matching result: if x < result: result = x + if result > isGeneric: result = isGeneric bindingRet result of tyOr: @@ -989,6 +1052,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # 'or' implies maximum matching result: if x > result: result = x if result >= isSubtype: + if result > isGeneric: result = isGeneric bindingRet result else: result = isNone @@ -1005,7 +1069,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = considerPreviousT: var concrete = concreteType(c, a) if concrete != nil and doBind: - put(c.bindings, f, concrete) + put(c, f, concrete) return isGeneric of tyBuiltInTypeClass: @@ -1013,7 +1077,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = let targetKind = f.sons[0].kind if targetKind == a.skipTypes({tyRange, tyGenericInst}).kind or (targetKind in {tyProc, tyPointer} and a.kind == tyNil): - put(c.bindings, f, a) + put(c, f, a) return isGeneric else: return isNone @@ -1022,7 +1086,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = considerPreviousT: result = matchUserTypeClass(c.c, c, f, aOrig) if result == isGeneric: - put(c.bindings, f, a) + put(c, f, a) of tyCompositeTypeClass: considerPreviousT: @@ -1038,7 +1102,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: result = typeRel(c, rootf.lastSon, a) if result != isNone: - put(c.bindings, f, a) + put(c, f, a) result = isGeneric of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) @@ -1072,7 +1136,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if doBind and result notin {isNone, isGeneric}: let concrete = concreteType(c, a) if concrete == nil: return isNone - put(c.bindings, f, concrete) + put(c, f, concrete) else: result = isGeneric @@ -1086,7 +1150,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if concrete == nil: return isNone if doBind: - put(c.bindings, f, concrete) + put(c, f, concrete) elif result > isGeneric: result = isGeneric elif a.kind == tyEmpty: @@ -1105,7 +1169,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if result != isNone and f.n != nil: if not exprStructuralEquivalent(f.n, aOrig.n): result = isNone - if result != isNone: put(c.bindings, f, aOrig) + if result != isNone: put(c, f, aOrig) else: result = isNone elif prev.kind == tyStatic: @@ -1133,7 +1197,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, f.base, a.base) if result != isNone: - put(c.bindings, f, a) + put(c, f, a) else: if tfUnresolved in f.flags: result = typeRel(c, prev.base, a) @@ -1151,7 +1215,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyStmt: if aOrig != nil and tfOldSchoolExprStmt notin f.flags: - put(c.bindings, f, aOrig) + put(c, f, aOrig) result = isGeneric of tyProxy: @@ -1256,6 +1320,13 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, if r == isGeneric: result.typ = getInstantiatedType(c, arg, m, base(f)) m.baseTypeMatch = true + # bug #4545: allow the call to go through a 'var T': + let vt = result.sons[0].typ.sons[1] + if vt.kind == tyVar: + let x = result.sons[1] + let va = newNodeIT(nkHiddenAddr, x.info, vt) + va.add x + result.sons[1] = va proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = case r @@ -1399,7 +1470,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: result = implicitConv(nkHiddenSubConv, f, arg, m, c) of isNone: - # do not do this in ``typeRel`` as it then can't infere T in ``ref T``: + # do not do this in ``typeRel`` as it then can't infer T in ``ref T``: if a.kind in {tyProxy, tyUnknown}: inc(m.genericMatches) m.fauxMatch = a.kind @@ -1420,6 +1491,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, m.baseTypeMatch = true else: result = userConvMatch(c, m, base(f), a, arg) + if result != nil: m.baseTypeMatch = true proc paramTypesMatch*(m: var TCandidate, f, a: PType, arg, argOrig: PNode): PNode = @@ -1532,13 +1604,13 @@ proc incrIndexType(t: PType) = assert t.kind == tyArrayConstr inc t.sons[0].n.sons[1].intVal -template isVarargsUntyped(x): expr = +template isVarargsUntyped(x): untyped = x.kind == tyVarargs and x.sons[0].kind == tyExpr and tfOldSchoolExprStmt notin x.sons[0].flags proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) = - template checkConstraint(n: expr) {.immediate, dirty.} = + template checkConstraint(n: untyped) {.dirty.} = if not formal.constraint.isNil: if matchNodeKinds(formal.constraint, n): # better match over other routines with no such restriction: @@ -1549,6 +1621,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if formal.typ.kind == tyVar: if not n.isLValue: m.state = csNoMatch + m.mutabilityProblem = uint8(f-1) return var @@ -1568,6 +1641,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, while a < n.len: if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped: + incl(marker, formal.position) if container.isNil: container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info)) setSon(m.call, formal.position + 1, container) @@ -1630,6 +1704,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # beware of the side-effects in 'prepareOperand'! So only do it for # varargs matching. See tests/metatype/tstatic_overloading. m.baseTypeMatch = false + incl(marker, formal.position) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) @@ -1653,30 +1728,39 @@ proc matchesAux(c: PContext, n, nOrig: PNode, when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) m.state = csNoMatch return - m.baseTypeMatch = false - n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) - var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, - n.sons[a], nOrig.sons[a]) - if arg == nil: - m.state = csNoMatch - return - if m.baseTypeMatch: - #assert(container == nil) + + if formal.typ.isVarargsUntyped: if container.isNil: - container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) + container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info)) + setSon(m.call, formal.position + 1, container) else: incrIndexType(container.typ) - addSon(container, arg) - setSon(m.call, formal.position + 1, - implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) - #if f != formalLen - 1: container = nil - - # pick the formal from the end, so that 'x, y, varargs, z' works: - f = max(f, formalLen - n.len + a + 1) + addSon(container, n.sons[a]) else: - setSon(m.call, formal.position + 1, arg) - inc(f) - container = nil + m.baseTypeMatch = false + n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) + var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, + n.sons[a], nOrig.sons[a]) + if arg == nil: + m.state = csNoMatch + return + if m.baseTypeMatch: + #assert(container == nil) + if container.isNil: + container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) + else: + incrIndexType(container.typ) + addSon(container, arg) + setSon(m.call, formal.position + 1, + implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) + #if f != formalLen - 1: container = nil + + # pick the formal from the end, so that 'x, y, varargs, z' works: + f = max(f, formalLen - n.len + a + 1) + else: + setSon(m.call, formal.position + 1, arg) + inc(f) + container = nil checkConstraint(n.sons[a]) inc(a) @@ -1707,15 +1791,18 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if formal.ast == nil: if formal.typ.kind == tyVarargs: var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info)) - addSon(m.call, implicitConv(nkHiddenStdConv, formal.typ, - container, m, c)) + setSon(m.call, formal.position + 1, + implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) else: # no default value m.state = csNoMatch break else: # use default value: - setSon(m.call, formal.position + 1, copyTree(formal.ast)) + var def = copyTree(formal.ast) + if def.kind == nkNilLit: + def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c) + setSon(m.call, formal.position + 1, def) inc(f) proc argtypeMatches*(c: PContext, f, a: PType): bool = @@ -1728,16 +1815,19 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool = result = res != nil proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; - op: TTypeAttachedOp): PSym {.procvar.} = + op: TTypeAttachedOp; col: int): PSym {.procvar.} = var m: TCandidate initCandidate(c, m, dc.typ) - var f = dc.typ.sons[1] + if col >= dc.typ.len: + localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") + return nil + var f = dc.typ.sons[col] if op == attachedDeepCopy: if f.kind in {tyRef, tyPtr}: f = f.lastSon else: if f.kind == tyVar: f = f.lastSon if typeRel(m, f, t) == isNone: - localError(info, errGenerated, "cannot instantiate 'deepCopy'") + localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") else: result = c.semGenerateInstance(c, dc, m.bindings, info) assert sfFromGeneric in result.flags @@ -1745,7 +1835,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; include suggest when not declared(tests): - template tests(s: stmt) {.immediate.} = discard + template tests(s: untyped) = discard tests: var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo()) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index bcab6b04a..c127b968c 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -138,7 +138,7 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) = suggestResult(symToSuggest(s, isLocal=true, $ideSug, 100)) inc outputs -template wholeSymTab(cond, section: expr) {.immediate.} = +template wholeSymTab(cond, section: untyped) = var isLocal = true for scope in walkScopes(c.currentScope): if scope == c.topLevelScope: isLocal = false @@ -393,6 +393,8 @@ proc suggestSym*(info: TLineInfo; s: PSym; isDecl=true) {.inline.} = proc markUsed(info: TLineInfo; s: PSym) = incl(s.flags, sfUsed) + if s.kind == skEnumField and s.owner != nil: + incl(s.owner.flags, sfUsed) if {sfDeprecated, sfError} * s.flags != {}: if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s) if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s) diff --git a/compiler/transf.nim b/compiler/transf.nim index a4a15ea4a..fc400c524 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -95,7 +95,7 @@ proc getCurrOwner(c: PTransf): PSym = proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info) - r.typ = skipTypes(typ, {tyGenericInst}) + r.typ = typ #skipTypes(typ, {tyGenericInst}) incl(r.flags, sfFromGeneric) let owner = getCurrOwner(c) if owner.isIterator and not c.tooEarly: @@ -204,14 +204,8 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode = if it.kind != nkConstDef: internalError(it.info, "transformConstSection") if it.sons[0].kind != nkSym: internalError(it.info, "transformConstSection") - if sfFakeConst in it[0].sym.flags: - var b = newNodeI(nkConstDef, it.info) - addSon(b, it[0]) - addSon(b, ast.emptyNode) # no type description - addSon(b, transform(c, it[2]).PNode) - result[i] = PTransNode(b) - else: - result[i] = PTransNode(it) + + result[i] = PTransNode(it) proc hasContinue(n: PNode): bool = case n.kind @@ -414,8 +408,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = result = newTransNode(nkChckRange, n, 3) dest = skipTypes(n.typ, abstractVar) result[0] = transform(c, n.sons[1]) - result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), source).PTransNode - result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode + result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), dest).PTransNode + result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), dest).PTransNode of tyFloat..tyFloat128: # XXX int64 -> float conversion? if skipTypes(n.typ, abstractVar).kind == tyRange: @@ -480,12 +474,14 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = type TPutArgInto = enum - paDirectMapping, paFastAsgn, paVarAsgn + paDirectMapping, paFastAsgn, paVarAsgn, paComplexOpenarray proc putArgInto(arg: PNode, formal: PType): TPutArgInto = # This analyses how to treat the mapping "formal <-> arg" in an # inline context. if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}: + if arg.kind == nkStmtListExpr: + return paComplexOpenarray return paDirectMapping # XXX really correct? # what if ``arg`` has side-effects? case arg.kind @@ -575,6 +571,14 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = assert(skipTypes(formal.typ, abstractInst).kind == tyVar) idNodeTablePut(newC.mapping, formal, arg) # XXX BUG still not correct if the arg has a side effect! + of paComplexOpenarray: + let typ = newType(tySequence, formal.owner) + addSonSkipIntLit(typ, formal.typ.sons[0]) + var temp = newTemp(c, typ, formal.info) + addVar(v, temp) + add(stmtList, newAsgnStmt(c, temp, arg.PTransNode)) + idNodeTablePut(newC.mapping, formal, temp) + var body = iter.getBody.copyTree pushInfoContext(n.info) # XXX optimize this somehow. But the check "c.inlining" is not correct: diff --git a/compiler/trees.nim b/compiler/trees.nim index 659df334b..fdd88c348 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -103,14 +103,16 @@ proc getMagic*(op: PNode): TMagic = else: result = mNone else: result = mNone -proc treeToSym*(t: PNode): PSym = - result = t.sym - proc isConstExpr*(n: PNode): bool = result = (n.kind in {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags) +proc isCaseObj*(n: PNode): bool = + if n.kind == nkRecCase: return true + for i in 0..<safeLen(n): + if n[i].isCaseObj: return true + proc isDeepConstExpr*(n: PNode): bool = case n.kind of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, @@ -119,11 +121,14 @@ proc isDeepConstExpr*(n: PNode): bool = of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure: - for i in 0 .. <n.len: + for i in ord(n.kind == nkObjConstr) .. <n.len: if not isDeepConstExpr(n.sons[i]): return false - # XXX once constant objects are supported by the codegen this needs to be - # weakened: - result = n.typ.isNil or n.typ.skipTypes({tyGenericInst, tyDistinct}).kind != tyObject + if n.typ.isNil: result = true + else: + let t = n.typ.skipTypes({tyGenericInst, tyDistinct}) + if t.kind in {tyRef, tyPtr}: return false + if t.kind != tyObject or not isCaseObj(t.n): + result = true else: discard proc flattenTreeAux(d, a: PNode, op: TMagic) = diff --git a/compiler/types.nim b/compiler/types.nim index 9aa991086..312e36ae5 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -48,6 +48,8 @@ proc isOrdinalType*(t: PType): bool proc enumHasHoles*(t: PType): bool const + # TODO: Remove tyTypeDesc from each abstractX and (where necessary) + # replace with typedescX abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable, tyTypeDesc} abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, @@ -61,6 +63,7 @@ const skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable, tyTypeDesc} + # typedescX is used if we're sure tyTypeDesc should be included (or skipped) typedescPtrs* = abstractPtrs + {tyTypeDesc} typedescInst* = abstractInst + {tyTypeDesc} @@ -148,10 +151,12 @@ proc skipGeneric(t: PType): PType = proc isOrdinalType(t: PType): bool = assert(t != nil) - # caution: uint, uint64 are no ordinal types! - result = t.kind in {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum} or - (t.kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and - isOrdinalType(t.sons[0]) + const + # caution: uint, uint64 are no ordinal types! + baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum} + parentKinds = {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst, + tyDistinct} + t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0])) proc enumHasHoles(t: PType): bool = var b = t @@ -407,7 +412,8 @@ const "!", "varargs[$1]", "iter[$1]", "Error Type", "BuiltInTypeClass", "UserTypeClass", "UserTypeClassInst", "CompositeTypeClass", - "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"] + "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor", + "void"] const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg} @@ -540,7 +546,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = else: result.add typeToString(t.sons[0]) of tyRange: - result = "range " & rangeToStr(t.n) + result = "range " + if t.n != nil and t.n.kind == nkRange: + result.add rangeToStr(t.n) if prefer != preferExported: result.add("(" & typeToString(t.sons[0]) & ")") of tyProc: @@ -727,6 +735,7 @@ proc equalParam(a, b: PSym): TParamsEquality = result = paramsNotEqual proc sameConstraints(a, b: PNode): bool = + if isNil(a) and isNil(b): return true internalAssert a.len == b.len for i in 1 .. <a.len: if not exprStructuralEquivalent(a[i].sym.constraint, @@ -799,8 +808,10 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = result = x.name.id == y.name.id if not result: break else: internalError(a.n.info, "sameTuple") + elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags: + result = false -template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = +template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) = if tfFromGeneric notin a.flags + b.flags: # fast case: id comparison suffices: result = a.id == b.id @@ -908,7 +919,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = while a.kind == tyDistinct: a = a.sons[0] if a.kind != b.kind: return false - if x.kind == tyGenericInst: + # this is required by tunique_type but makes no sense really: + if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags: let lhs = x.skipGenericAlias rhs = y.skipGenericAlias @@ -922,7 +934,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = case a.kind of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, - tyInt..tyBigNum, tyStmt, tyExpr: + tyInt..tyBigNum, tyStmt, tyExpr, tyVoid: result = sameFlags(a, b) of tyStatic, tyFromExpr: result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) @@ -1108,7 +1120,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = nil of tyExpr, tyStmt, tyStatic: if kind notin {skParam, skResult}: result = t - of tyEmpty: + of tyVoid: if taField notin flags: result = t of tyTypeClasses: if not (tfGenericTypeParam in t.flags or taField notin flags): result = t @@ -1153,7 +1165,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if result != nil: break if result.isNil and t.n != nil: result = typeAllowedNode(marker, t.n, kind, flags) - of tyProxy: + of tyProxy, tyEmpty: # for now same as error node; we say it's a valid type as it should # prevent cascading errors: result = nil @@ -1313,6 +1325,9 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = of tyTypeDesc: result = computeSizeAux(typ.base, a) of tyForward: return szIllegalRecursion + of tyStatic: + if typ.n != nil: result = computeSizeAux(lastSon(typ), a) + else: result = szUnknownSize else: #internalError("computeSizeAux()") result = szUnknownSize @@ -1448,6 +1463,18 @@ proc skipConv*(n: PNode): PNode = result = n.sons[1] else: discard +proc skipHidden*(n: PNode): PNode = + result = n + while true: + case result.kind + of nkHiddenStdConv, nkHiddenSubConv: + if result.sons[1].typ.classify == result.typ.classify: + result = result.sons[1] + else: break + of nkHiddenDeref, nkHiddenAddr: + result = result.sons[0] + else: break + proc skipConvTakeType*(n: PNode): PNode = result = n.skipConv result.typ = n.typ @@ -1493,3 +1520,12 @@ proc skipHiddenSubConv*(n: PNode): PNode = result.typ = dest else: result = n + +proc typeMismatch*(n: PNode, formal, actual: PType) = + if formal.kind != tyError and actual.kind != tyError: + let named = typeToString(formal) + let desc = typeToString(formal, preferDesc) + let x = if named == desc: named else: named & " = " & desc + localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & + typeToString(actual) & ") " & + `%`(msgKindToString(errButExpectedX), [x])) diff --git a/compiler/vm.nim b/compiler/vm.nim index 7220e1b8e..5accf628e 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -10,7 +10,9 @@ ## This file implements the new evaluation engine for Nim code. ## An instruction is 1-3 int32s in memory, it is a register based VM. -const debugEchoCode = false +const + debugEchoCode = false + traceCode = debugEchoCode import ast except getstr @@ -90,38 +92,38 @@ when not defined(nimComputedGoto): proc myreset(n: var TFullReg) = reset(n) -template ensureKind(k: expr) {.immediate, dirty.} = +template ensureKind(k: untyped) {.dirty.} = if regs[ra].kind != k: myreset(regs[ra]) regs[ra].kind = k -template decodeB(k: expr) {.immediate, dirty.} = +template decodeB(k: untyped) {.dirty.} = let rb = instr.regB ensureKind(k) -template decodeBC(k: expr) {.immediate, dirty.} = +template decodeBC(k: untyped) {.dirty.} = let rb = instr.regB let rc = instr.regC ensureKind(k) -template declBC() {.immediate, dirty.} = +template declBC() {.dirty.} = let rb = instr.regB let rc = instr.regC -template decodeBImm(k: expr) {.immediate, dirty.} = +template decodeBImm(k: untyped) {.dirty.} = let rb = instr.regB let imm = instr.regC - byteExcess ensureKind(k) -template decodeBx(k: expr) {.immediate, dirty.} = +template decodeBx(k: untyped) {.dirty.} = let rbx = instr.regBx - wordExcess ensureKind(k) -template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) +template move(a, b: untyped) {.dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler proc createStrKeepNode(x: var TFullReg; keepNode=true) = - if x.node.isNil: + if x.node.isNil or not keepNode: x.node = newNode(nkStrLit) elif x.node.kind == nkNilLit and keepNode: when defined(useNodeIds): @@ -160,7 +162,7 @@ proc moveConst(x: var TFullReg, y: TFullReg) = # this seems to be the best way to model the reference semantics # of system.NimNode: -template asgnRef(x, y: expr) = moveConst(x, y) +template asgnRef(x, y: untyped) = moveConst(x, y) proc copyValue(src: PNode): PNode = if src == nil or nfIsRef in src.flags: @@ -231,7 +233,7 @@ proc regToNode(x: TFullReg): PNode = of rkRegisterAddr: result = regToNode(x.regAddr[]) of rkNodeAddr: result = x.nodeAddr[] -template getstr(a: expr): expr = +template getstr(a: untyped): untyped = (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = @@ -404,7 +406,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let instr = c.code[pc] let ra = instr.regA #if c.traceActive: - #echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC + when traceCode: + echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC # message(c.debug[pc], warnUser, "Trace") case instr.opcode @@ -542,7 +545,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rb].node.kind == nkRefTy: regs[ra].node = regs[rb].node.sons[0] else: - stackTrace(c, tos, pc, errGenerated, "limited VM support for 'ref'") + stackTrace(c, tos, pc, errGenerated, "limited VM support for pointers") else: stackTrace(c, tos, pc, errNilAccess) of opcWrDeref: @@ -1182,19 +1185,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetType: let rb = instr.regB let rc = instr.regC - if rc == 0: + case rc: + of 0: + # getType opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc]) else: stackTrace(c, tos, pc, errGenerated, "node has no type") - else: + of 1: # typeKind opcode: ensureKind(rkInt) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].intVal = ord(regs[rb].node.typ.kind) #else: # stackTrace(c, tos, pc, errGenerated, "node has no type") + of 2: + # getTypeInst opcode: + ensureKind(rkNode) + if regs[rb].kind == rkNode and regs[rb].node.typ != nil: + regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc]) + else: + stackTrace(c, tos, pc, errGenerated, "node has no type") + else: + # getTypeImpl opcode: + ensureKind(rkNode) + if regs[rb].kind == rkNode and regs[rb].node.typ != nil: + regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc]) + else: + stackTrace(c, tos, pc, errGenerated, "node has no type") of opcNStrVal: decodeB(rkNode) createStr regs[ra] @@ -1554,7 +1573,7 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg = var evalMacroCounter: int proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = - # XXX GlobalError() is ugly here, but I don't know a better solution for now + # XXX globalError() is ugly here, but I don't know a better solution for now inc(evalMacroCounter) if evalMacroCounter > 100: globalError(n.info, errTemplateInstantiationTooNested) @@ -1584,7 +1603,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # return value: tos.slots[0].kind = rkNode - tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) + tos.slots[0].node = newNodeI(nkEmpty, n.info) # setup parameters: for i in 1.. <sym.typ.len: @@ -1604,4 +1623,3 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = if cyclicTree(result): globalError(n.info, errCyclicTree) dec(evalMacroCounter) c.callsite = nil - #debug result diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 337e4ec8f..83c1dbf43 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -230,10 +230,10 @@ const slotSomeTemp* = slotTempUnknown relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} -template opcode*(x: TInstr): TOpcode {.immediate.} = TOpcode(x.uint32 and 0xff'u32) -template regA*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 8'u32 and 0xff'u32) -template regB*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 16'u32 and 0xff'u32) -template regC*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 24'u32) -template regBx*(x: TInstr): int {.immediate.} = (x.uint32 shr 16'u32).int +template opcode*(x: TInstr): TOpcode = TOpcode(x.uint32 and 0xff'u32) +template regA*(x: TInstr): TRegister = TRegister(x.uint32 shr 8'u32 and 0xff'u32) +template regB*(x: TInstr): TRegister = TRegister(x.uint32 shr 16'u32 and 0xff'u32) +template regC*(x: TInstr): TRegister = TRegister(x.uint32 shr 24'u32) +template regBx*(x: TInstr): int = (x.uint32 shr 16'u32).int -template jmpDiff*(x: TInstr): int {.immediate.} = regBx(x) - wordExcess +template jmpDiff*(x: TInstr): int = regBx(x) - wordExcess diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index a4f02092d..7dbf6f801 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -import ast, types, msgs, osproc, streams, options, idents, securehash +import ast, types, msgs, os, osproc, streams, options, idents, securehash proc readOutput(p: Process): string = result = "" @@ -51,7 +51,9 @@ proc opGorge*(cmd, input, cache: string): string = proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = try: - let filename = file.findFile + var filename = parentDir(info.toFullPath) / file + if not fileExists(filename): + filename = file.findFile result = readFile(filename) # we produce a fake include statement for every slurped filename, so that # the module dependencies are accurate: @@ -61,123 +63,249 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = localError(info, errCannotOpenFile, file) result = "" -proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode = +proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode = let sym = newSym(skType, getIdent(name), t.owner, info) + sym.magic = m sym.typ = t result = newSymNode(sym) result.typ = t -proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode +proc mapTypeToAstX(t: PType; info: TLineInfo; + inst=false; allowRecursionX=false): PNode -proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode = +proc mapTypeToBracketX(name: string; m: TMagic; t: PType; info: TLineInfo; + inst=false): PNode = result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - result.add atomicTypeX(name, t, info) + result.add atomicTypeX(name, m, t, info) for i in 0 .. < t.len: if t.sons[i] == nil: - let void = atomicTypeX("void", t, info) - void.typ = newType(tyEmpty, t.owner) + let void = atomicTypeX("void", mVoid, t, info) + void.typ = newType(tyVoid, t.owner) result.add void else: - result.add mapTypeToAst(t.sons[i], info) + result.add mapTypeToAstX(t.sons[i], info, inst) + +proc objectNode(n: PNode): PNode = + if n.kind == nkSym: + result = newNodeI(nkIdentDefs, n.info) + result.add n # name + result.add mapTypeToAstX(n.sym.typ, n.info, true, false) # type + result.add ast.emptyNode # no assigned value + else: + result = copyNode(n) + for i in 0 ..< n.safeLen: + result.add objectNode(n[i]) + +proc mapTypeToAstX(t: PType; info: TLineInfo; + inst=false; allowRecursionX=false): PNode = + var allowRecursion = allowRecursionX + template atomicType(name, m): untyped = atomicTypeX(name, m, t, info) + template mapTypeToAst(t,info): untyped = mapTypeToAstX(t, info, inst) + template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true) + template mapTypeToAst(t,i,info): untyped = + if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst) + else: ast.emptyNode + template mapTypeToBracket(name, m, t, info): untyped = + mapTypeToBracketX(name, m, t, info, inst) + template newNodeX(kind): untyped = + newNodeIT(kind, if t.n.isNil: info else: t.n.info, t) + template newIdent(s): untyped = + var r = newNodeX(nkIdent) + r.add !s + r + template newIdentDefs(n,t): untyped = + var id = newNodeX(nkIdentDefs) + id.add n # name + id.add mapTypeToAst(t, info) # type + id.add ast.emptyNode # no assigned value + id + template newIdentDefs(s): untyped = newIdentDefs(s, s.typ) -proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode = - template atomicType(name): expr = atomicTypeX(name, t, info) + if inst: + if t.sym != nil: # if this node has a symbol + if allowRecursion: # getTypeImpl behavior: turn off recursion + allowRecursion = false + else: # getTypeInst behavior: return symbol + return atomicType(t.sym.name.s, t.sym.magic) case t.kind - of tyNone: result = atomicType("none") - of tyBool: result = atomicType("bool") - of tyChar: result = atomicType("char") - of tyNil: result = atomicType("nil") - of tyExpr: result = atomicType("expr") - of tyStmt: result = atomicType("stmt") - of tyEmpty: result = atomicType"void" + of tyNone: result = atomicType("none", mNone) + of tyBool: result = atomicType("bool", mBool) + of tyChar: result = atomicType("char", mChar) + of tyNil: result = atomicType("nil", mNil) + of tyExpr: result = atomicType("expr", mExpr) + of tyStmt: result = atomicType("stmt", mStmt) + of tyVoid: result = atomicType("void", mVoid) + of tyEmpty: result = atomicType("empty", mNone) of tyArrayConstr, tyArray: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - result.add atomicType("array") - result.add mapTypeToAst(t.sons[0], info) + result.add atomicType("array", mArray) + if inst and t.sons[0].kind == tyRange: + var rng = newNodeX(nkInfix) + rng.add newIdentNode(getIdent(".."), info) + rng.add t.sons[0].n.sons[0].copyTree + rng.add t.sons[0].n.sons[1].copyTree + result.add rng + else: + result.add mapTypeToAst(t.sons[0], info) result.add mapTypeToAst(t.sons[1], info) of tyTypeDesc: if t.base != nil: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - result.add atomicType("typeDesc") + result.add atomicType("typeDesc", mTypeDesc) result.add mapTypeToAst(t.base, info) else: - result = atomicType"typeDesc" + result = atomicType("typeDesc", mTypeDesc) of tyGenericInvocation: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) for i in 0 .. < t.len: result.add mapTypeToAst(t.sons[i], info) - of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst: + of tyGenericInst: + if inst: + if allowRecursion: + result = mapTypeToAstR(t.lastSon, info) + else: + result = newNodeX(nkBracketExpr) + result.add mapTypeToAst(t.lastSon, info) + for i in 1 .. < t.len-1: + result.add mapTypeToAst(t.sons[i], info) + else: + result = mapTypeToAst(t.lastSon, info) + of tyGenericBody, tyOrdinal, tyUserTypeClassInst: result = mapTypeToAst(t.lastSon, info) of tyDistinct: - if allowRecursion: - result = mapTypeToBracket("distinct", t, info) + if inst: + result = newNodeX(nkDistinctTy) + result.add mapTypeToAst(t.sons[0], info) else: - result = atomicType(t.sym.name.s) - of tyGenericParam, tyForward: result = atomicType(t.sym.name.s) + if allowRecursion or t.sym == nil: + result = mapTypeToBracket("distinct", mDistinct, t, info) + else: + result = atomicType(t.sym.name.s, t.sym.magic) + of tyGenericParam, tyForward: + result = atomicType(t.sym.name.s, t.sym.magic) of tyObject: - if allowRecursion: - result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t) - if t.sons[0] == nil: - result.add ast.emptyNode + if inst: + result = newNodeX(nkObjectTy) + result.add ast.emptyNode # pragmas not reconstructed yet + if t.sons[0] == nil: result.add ast.emptyNode # handle parent object else: - result.add mapTypeToAst(t.sons[0], info) - result.add copyTree(t.n) + var nn = newNodeX(nkOfInherit) + nn.add mapTypeToAst(t.sons[0], info) + result.add nn + if t.n.len > 0: + result.add objectNode(t.n) + else: + result.add ast.emptyNode else: - result = atomicType(t.sym.name.s) + if allowRecursion or t.sym == nil: + result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t) + result.add ast.emptyNode + if t.sons[0] == nil: + result.add ast.emptyNode + else: + result.add mapTypeToAst(t.sons[0], info) + result.add copyTree(t.n) + else: + result = atomicType(t.sym.name.s, t.sym.magic) of tyEnum: result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t) result.add copyTree(t.n) - of tyTuple: result = mapTypeToBracket("tuple", t, info) - of tySet: result = mapTypeToBracket("set", t, info) - of tyPtr: result = mapTypeToBracket("ptr", t, info) - of tyRef: result = mapTypeToBracket("ref", t, info) - of tyVar: result = mapTypeToBracket("var", t, info) - of tySequence: result = mapTypeToBracket("seq", t, info) - of tyProc: result = mapTypeToBracket("proc", t, info) - of tyOpenArray: result = mapTypeToBracket("openArray", t, info) + of tyTuple: + if inst: + result = newNodeX(nkTupleTy) + for s in t.n.sons: + result.add newIdentDefs(s) + else: + result = mapTypeToBracket("tuple", mTuple, t, info) + of tySet: result = mapTypeToBracket("set", mSet, t, info) + of tyPtr: + if inst: + result = newNodeX(nkPtrTy) + result.add mapTypeToAst(t.sons[0], info) + else: + result = mapTypeToBracket("ptr", mPtr, t, info) + of tyRef: + if inst: + result = newNodeX(nkRefTy) + result.add mapTypeToAst(t.sons[0], info) + else: + result = mapTypeToBracket("ref", mRef, t, info) + of tyVar: result = mapTypeToBracket("var", mVar, t, info) + of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) + of tyProc: + if inst: + result = newNodeX(nkProcTy) + var fp = newNodeX(nkFormalParams) + if t.sons[0] == nil: + fp.add ast.emptyNode + else: + fp.add mapTypeToAst(t.sons[0], t.n[0].info) + for i in 1..<t.sons.len: + fp.add newIdentDefs(t.n[i], t.sons[i]) + result.add fp + result.add ast.emptyNode # pragmas aren't reconstructed yet + else: + result = mapTypeToBracket("proc", mNone, t, info) + of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info) of tyRange: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - result.add atomicType("range") + result.add atomicType("range", mRange) result.add t.n.sons[0].copyTree result.add t.n.sons[1].copyTree - of tyPointer: result = atomicType"pointer" - of tyString: result = atomicType"string" - of tyCString: result = atomicType"cstring" - of tyInt: result = atomicType"int" - of tyInt8: result = atomicType"int8" - of tyInt16: result = atomicType"int16" - of tyInt32: result = atomicType"int32" - of tyInt64: result = atomicType"int64" - of tyFloat: result = atomicType"float" - of tyFloat32: result = atomicType"float32" - of tyFloat64: result = atomicType"float64" - of tyFloat128: result = atomicType"float128" - of tyUInt: result = atomicType"uint" - of tyUInt8: result = atomicType"uint8" - of tyUInt16: result = atomicType"uint16" - of tyUInt32: result = atomicType"uint32" - of tyUInt64: result = atomicType"uint64" - of tyBigNum: result = atomicType"bignum" - of tyConst: result = mapTypeToBracket("const", t, info) - of tyMutable: result = mapTypeToBracket("mutable", t, info) - of tyVarargs: result = mapTypeToBracket("varargs", t, info) - of tyIter: result = mapTypeToBracket("iter", t, info) - of tyProxy: result = atomicType"error" - of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info) + of tyPointer: result = atomicType("pointer", mPointer) + of tyString: result = atomicType("string", mString) + of tyCString: result = atomicType("cstring", mCString) + of tyInt: result = atomicType("int", mInt) + of tyInt8: result = atomicType("int8", mInt8) + of tyInt16: result = atomicType("int16", mInt16) + of tyInt32: result = atomicType("int32", mInt32) + of tyInt64: result = atomicType("int64", mInt64) + of tyFloat: result = atomicType("float", mFloat) + of tyFloat32: result = atomicType("float32", mFloat32) + of tyFloat64: result = atomicType("float64", mFloat64) + of tyFloat128: result = atomicType("float128", mFloat128) + of tyUInt: result = atomicType("uint", mUint) + of tyUInt8: result = atomicType("uint8", mUint8) + of tyUInt16: result = atomicType("uint16", mUint16) + of tyUInt32: result = atomicType("uint32", mUint32) + of tyUInt64: result = atomicType("uint64", mUint64) + of tyBigNum: result = atomicType("bignum", mNone) + of tyConst: result = mapTypeToBracket("const", mNone, t, info) + of tyMutable: result = mapTypeToBracket("mutable", mNone, t, info) + of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info) + of tyIter: result = mapTypeToBracket("iter", mNone, t, info) + of tyProxy: result = atomicType("error", mNone) + of tyBuiltInTypeClass: + result = mapTypeToBracket("builtinTypeClass", mNone, t, info) of tyUserTypeClass: - result = mapTypeToBracket("concept", t, info) + result = mapTypeToBracket("concept", mNone, t, info) result.add t.n.copyTree - of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info) - of tyAnd: result = mapTypeToBracket("and", t, info) - of tyOr: result = mapTypeToBracket("or", t, info) - of tyNot: result = mapTypeToBracket("not", t, info) - of tyAnything: result = atomicType"anything" + of tyCompositeTypeClass: + result = mapTypeToBracket("compositeTypeClass", mNone, t, info) + of tyAnd: result = mapTypeToBracket("and", mAnd, t, info) + of tyOr: result = mapTypeToBracket("or", mOr, t, info) + of tyNot: result = mapTypeToBracket("not", mNot, t, info) + of tyAnything: result = atomicType("anything", mNone) of tyStatic, tyFromExpr, tyFieldAccessor: - result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - result.add atomicType("static") - if t.n != nil: - result.add t.n.copyTree + if inst: + if t.n != nil: result = t.n.copyTree + else: result = atomicType("void", mVoid) + else: + result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) + result.add atomicType("static", mNone) + if t.n != nil: + result.add t.n.copyTree proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode = - result = mapTypeToAst(t, info, true) + result = mapTypeToAstX(t, info, false, true) + +# the "Inst" version includes generic parameters in the resulting type tree +# and also tries to look like the corresponding Nim type declaration +proc opMapTypeInstToAst*(t: PType; info: TLineInfo): PNode = + result = mapTypeToAstX(t, info, true, false) + +# the "Impl" version includes generic parameters in the resulting type tree +# and also tries to look like the corresponding Nim type implementation +proc opMapTypeImplToAst*(t: PType; info: TLineInfo): PNode = + result = mapTypeToAstX(t, info, true, true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 47f16a013..61ab65360 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -102,6 +102,10 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = let ins = (opc.uint32 or (a.uint32 shl 8'u32) or (b.uint32 shl 16'u32) or (c.uint32 shl 24'u32)).TInstr + when false: + if ctx.code.len == 43: + writeStackTrace() + echo "generating ", opc ctx.code.add(ins) ctx.debug.add(n.info) @@ -122,6 +126,11 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = # Applies `opc` to `bx` and stores it into register `a` # `bx` must be signed and in the range [-32768, 32767] + when false: + if c.code.len == 43: + writeStackTrace() + echo "generating ", opc + if bx >= -32768 and bx <= 32767: let ins = (opc.uint32 or a.uint32 shl 8'u32 or (bx+wordExcess).uint32 shl 16'u32).TInstr @@ -222,7 +231,7 @@ proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister = proc freeTempRange(c: PCtx; start: TRegister, n: int) = for i in start .. start+n-1: c.freeTemp(TRegister(i)) -template withTemp(tmp, typ: expr, body: stmt) {.immediate, dirty.} = +template withTemp(tmp, typ, body: untyped) {.dirty.} = var tmp = getTemp(c, typ) body c.freeTemp(tmp) @@ -232,7 +241,7 @@ proc popBlock(c: PCtx; oldLen: int) = c.patch(f) c.prc.blocks.setLen(oldLen) -template withBlock(labl: PSym; body: stmt) {.immediate, dirty.} = +template withBlock(labl: PSym; body: untyped) {.dirty.} = var oldLen {.gensym.} = c.prc.blocks.len c.prc.blocks.add TBlock(label: labl, fixups: @[]) body @@ -258,7 +267,7 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. # So we only set dest to -1 if 'void': - if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty): + if dest >= 0 and (n.typ.isNil or n.typ.kind == tyVoid): c.freeTemp(dest) dest = -1 @@ -365,7 +374,7 @@ proc canonValue*(n: PNode): PNode = proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len - assert(n.kind != nkCall) + #assert(n.kind != nkCall) n.flags.incl nfAllConst c.constants.add n.canonValue internalAssert result < 0x7fff @@ -488,12 +497,30 @@ proc genReturn(c: PCtx; n: PNode) = gen(c, n.sons[0]) c.gABC(n, opcRet) + +proc genLit(c: PCtx; n: PNode; dest: var TDest) = + # opcLdConst is now always valid. We produce the necessary copy in the + # assignments now: + #var opc = opcLdConst + if dest < 0: dest = c.getTemp(n.typ) + #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst + let lit = genLiteral(c, n) + c.gABx(n, opcLdConst, dest, lit) + proc genCall(c: PCtx; n: PNode; dest: var TDest) = + # it can happen that due to inlining we have a 'n' that should be + # treated as a constant (see issue #537). + #if n.typ != nil and n.typ.sym != nil and n.typ.sym.magic == mPNimrodNode: + # genLit(c, n, dest) + # return if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) let x = c.getTempRange(n.len, slotTempUnknown) # varargs need 'opcSetType' for the FFI support: - let fntyp = n.sons[0].typ + let fntyp = skipTypes(n.sons[0].typ, abstractInst) for i in 0.. <n.len: + #if i > 0 and i < sonsLen(fntyp): + # let paramType = fntyp.n.sons[i] + # if paramType.typ.isCompileTimeOnly: continue var r: TRegister = x+i c.gen(n.sons[i], r) if i >= fntyp.len: @@ -570,16 +597,27 @@ proc genNew(c: PCtx; n: PNode) = c.freeTemp(dest) proc genNewSeq(c: PCtx; n: PNode) = - let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ) + let t = n.sons[1].typ + let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(t) else: c.genx(n.sons[1]) let tmp = c.genx(n.sons[2]) - c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes( + c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes( abstractVar-{tyTypeDesc}))) c.gABx(n, opcNewSeq, tmp, 0) c.freeTemp(tmp) c.genAsgnPatch(n.sons[1], dest) c.freeTemp(dest) +proc genNewSeqOfCap(c: PCtx; n: PNode; dest: var TDest) = + let t = n.typ + let tmp = c.getTemp(n.sons[1].typ) + c.gABx(n, opcLdNull, dest, c.genType(t)) + c.gABx(n, opcLdImmInt, tmp, 0) + c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes( + abstractVar-{tyTypeDesc}))) + c.gABx(n, opcNewSeq, tmp, 0) + c.freeTemp(tmp) + proc genUnaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) @@ -626,8 +664,7 @@ proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = 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: - if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or - (t.kind == tyInt and t.size == 4): + if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = @@ -704,6 +741,10 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.genNarrow(n, dest) proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = + if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: + # don't do anything for lambda lifting conversions: + gen(c, arg, dest) + return let tmp = c.genx(arg) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) @@ -751,6 +792,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNewSeq: unused(n, dest) c.genNewSeq(n) + of mNewSeqOfCap: c.genNewSeqOfCap(n, dest) of mNewString: genUnaryABC(c, n, dest, opcNewStr) # XXX buggy @@ -804,7 +846,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mLtF64: genBinaryABC(c, n, dest, opcLtFloat) of mLePtr, mLeU, mLeU64: genBinaryABC(c, n, dest, opcLeu) of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu) - of mEqProc, mEqRef, mEqUntracedRef, mEqCString: + of mEqProc, mEqRef, mEqUntracedRef: genBinaryABC(c, n, dest, opcEqRef) of mXor: genBinaryABCnarrowU(c, n, dest, opcXor) of mNot: genUnaryABC(c, n, dest, opcNot) @@ -821,7 +863,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n.sons[1], dest) - of mEqStr: genBinaryABC(c, n, dest, opcEqStr) + of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) of mEqSet: genBinarySet(c, n, dest, opcEqSet) @@ -970,7 +1012,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNGetType: let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) - c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0) + let rc = case n[0].sym.name.s: + of "getType": 0 + of "typeKind": 1 + of "getTypeInst": 2 + else: 3 # "getTypeImpl" + c.gABC(n, opcNGetType, dest, tmp, rc) c.freeTemp(tmp) #genUnaryABC(c, n, dest, opcNGetType) of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal) @@ -1117,7 +1164,10 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; if isAddr and (let m = canElimAddr(n); m != nil): gen(c, m, dest, flags) return - let newflags = if isAddr: flags+{gfAddrOf} else: flags + + let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess} + else: {gfAddrOf} + let newflags = if isAddr: flags+af else: flags # consider: # proc foo(f: var ref int) = # f = new(int) @@ -1132,7 +1182,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; if gfAddrOf notin flags and fitsRegister(n.typ): c.gABC(n, opcNodeToReg, dest, dest) elif isAddr and isGlobal(n.sons[0]): - gen(c, n.sons[0], dest, flags+{gfAddrOf}) + gen(c, n.sons[0], dest, flags+af) else: let tmp = c.genx(n.sons[0], newflags) if dest < 0: dest = c.getTemp(n.typ) @@ -1215,7 +1265,7 @@ proc checkCanEval(c: PCtx; n: PNode) = proc isTemp(c: PCtx; dest: TDest): bool = result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown -template needsAdditionalCopy(n): expr = +template needsAdditionalCopy(n): untyped = not c.isTemp(dest) and not fitsRegister(n.typ) proc skipDeref(n: PNode): PNode = @@ -1286,15 +1336,6 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let dest = c.genx(le, {gfAddrOf}) genAsgn(c, dest, ri, requiresCopy) -proc genLit(c: PCtx; n: PNode; dest: var TDest) = - # opcLdConst is now always valid. We produce the necessary copy in the - # assignments now: - #var opc = opcLdConst - if dest < 0: dest = c.getTemp(n.typ) - #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst - let lit = genLiteral(c, n) - c.gABx(n, opcLdConst, dest, lit) - proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = var n = newNode(nkType) n.typ = t @@ -1321,10 +1362,11 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = # var decls{.compileTime.}: seq[NimNode] = @[] let dest = c.getTemp(s.typ) c.gABx(n, opcLdGlobal, dest, s.position) - let tmp = c.genx(s.ast) - c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp) - c.freeTemp(dest) - c.freeTemp(tmp) + if s.ast != nil: + let tmp = c.genx(s.ast) + c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp) + c.freeTemp(dest) + c.freeTemp(tmp) proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let s = n.sym @@ -1361,7 +1403,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = # see tests/t99bott for an example that triggers it: cannotEval(n) -template needsRegLoad(): expr = +template needsRegLoad(): untyped = gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})) proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; @@ -1447,12 +1489,12 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = of tyObject: result = newNodeIT(nkObjConstr, info, t) result.add(newNodeIT(nkEmpty, info, t)) - getNullValueAux(t.n, result) # initialize inherited fields: var base = t.sons[0] while base != nil: getNullValueAux(skipTypes(base, skipPtrs).n, result) base = base.sons[0] + getNullValueAux(t.n, result) of tyArray, tyArrayConstr: result = newNodeIT(nkBracket, info, t) for i in countup(0, int(lengthOrd(t)) - 1): @@ -1655,10 +1697,10 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of skType: genTypeLit(c, s.typ, dest) of skGenericParam: - if c.prc.sym.kind == skMacro: + if c.prc.sym != nil and c.prc.sym.kind == skMacro: genRdVar(c, n, dest, flags) else: - internalError(n.info, "cannot generate code for: " & s.name.s) + globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s) else: globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s) of nkCallKinds: @@ -1736,9 +1778,9 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of declarativeDefs: unused(n, dest) of nkLambdaKinds: - let s = n.sons[namePos].sym - discard genProc(c, s) - genLit(c, n.sons[namePos], dest) + #let s = n.sons[namePos].sym + #discard genProc(c, s) + genLit(c, newSymNode(n.sons[namePos].sym), dest) of nkChckRangeF, nkChckRange64, nkChckRange: let tmp0 = c.genx(n.sons[0]) @@ -1766,6 +1808,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genConv(c, n, n.sons[1], dest, opcCast) else: globalError(n.info, errGenerated, "VM is not allowed to 'cast'") + of nkTypeOfExpr: + genTypeLit(c, n.typ, dest) else: globalError(n.info, errGenerated, "cannot generate VM code for " & $n) @@ -1865,8 +1909,8 @@ proc optimizeJumps(c: PCtx; start: int) = else: discard proc genProc(c: PCtx; s: PSym): int = - let x = s.ast.sons[optimizedCodePos] - if x.kind == nkEmpty: + var x = s.ast.sons[miscPos] + if x.kind == nkEmpty or x[0].kind == nkEmpty: #if s.name.s == "outterMacro" or s.name.s == "innerProc": # echo "GENERATING CODE FOR ", s.name.s let last = c.code.len-1 @@ -1877,7 +1921,11 @@ proc genProc(c: PCtx; s: PSym): int = c.debug.setLen(last) #c.removeLastEof result = c.code.len+1 # skip the jump instruction - s.ast.sons[optimizedCodePos] = newIntNode(nkIntLit, result) + if x.kind == nkEmpty: + x = newTree(nkBracket, newIntNode(nkIntLit, result), ast.emptyNode) + else: + x.sons[0] = newIntNode(nkIntLit, result) + s.ast.sons[miscPos] = x # thanks to the jmp we can add top level statements easily and also nest # procs easily: let body = s.getBody @@ -1912,4 +1960,4 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = oldPrc else: c.prc.maxSlots = s.offset - result = x.intVal.int + result = x[0].intVal.int diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 3456e893b..548a3af97 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -template setX(k, field) {.immediate, dirty.} = +template setX(k, field) {.dirty.} = var s: seq[TFullReg] move(s, cast[seq[TFullReg]](a.slots)) if s[a.ra].kind != k: @@ -48,7 +48,7 @@ proc setResult*(a: VmArgs; v: seq[string]) = for x in v: n.add newStrNode(nkStrLit, x) s[a.ra].node = n -template getX(k, field) {.immediate, dirty.} = +template getX(k, field) {.dirty.} = doAssert i < a.rc-1 let s = cast[seq[TFullReg]](a.slots) doAssert s[i+a.rb+1].kind == k diff --git a/compiler/vmops.nim b/compiler/vmops.nim index e40e05eff..1c2725a98 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -15,31 +15,36 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, from os import getEnv, existsEnv, dirExists, fileExists, walkDir -template mathop(op) {.immediate, dirty.} = +template mathop(op) {.dirty.} = registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`) -template osop(op) {.immediate, dirty.} = +template osop(op) {.dirty.} = registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`) -template systemop(op) {.immediate, dirty.} = +template systemop(op) {.dirty.} = registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`) -template wrap1f(op) {.immediate, dirty.} = +template wrap1f_math(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getFloat(a, 0))) mathop op -template wrap2f(op) {.immediate, dirty.} = +template wrap2f_math(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getFloat(a, 0), getFloat(a, 1))) mathop op -template wrap1s(op) {.immediate, dirty.} = +template wrap1s_os(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getString(a, 0))) osop op -template wrap2svoid(op) {.immediate, dirty.} = +template wrap1s_system(op) {.dirty.} = + proc `op Wrapper`(a: VmArgs) {.nimcall.} = + setResult(a, op(getString(a, 0))) + systemop op + +template wrap2svoid_system(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = op(getString(a, 0), getString(a, 1)) systemop op @@ -55,34 +60,35 @@ proc staticWalkDirImpl(path: string, relative: bool): PNode = newStrNode(nkStrLit, f)) proc registerAdditionalOps*(c: PCtx) = - wrap1f(sqrt) - wrap1f(ln) - wrap1f(log10) - wrap1f(log2) - wrap1f(exp) - wrap1f(round) - wrap1f(arccos) - wrap1f(arcsin) - wrap1f(arctan) - wrap2f(arctan2) - wrap1f(cos) - wrap1f(cosh) - wrap2f(hypot) - wrap1f(sinh) - wrap1f(sin) - wrap1f(tan) - wrap1f(tanh) - wrap2f(pow) - wrap1f(trunc) - wrap1f(floor) - wrap1f(ceil) - wrap2f(fmod) + wrap1f_math(sqrt) + wrap1f_math(ln) + wrap1f_math(log10) + wrap1f_math(log2) + wrap1f_math(exp) + wrap1f_math(round) + wrap1f_math(arccos) + wrap1f_math(arcsin) + wrap1f_math(arctan) + wrap2f_math(arctan2) + wrap1f_math(cos) + wrap1f_math(cosh) + wrap2f_math(hypot) + wrap1f_math(sinh) + wrap1f_math(sin) + wrap1f_math(tan) + wrap1f_math(tanh) + wrap2f_math(pow) + wrap1f_math(trunc) + wrap1f_math(floor) + wrap1f_math(ceil) + wrap2f_math(fmod) - wrap1s(getEnv) - wrap1s(existsEnv) - wrap1s(dirExists) - wrap1s(fileExists) - wrap2svoid(writeFile) + wrap1s_os(getEnv) + wrap1s_os(existsEnv) + wrap1s_os(dirExists) + wrap1s_os(fileExists) + wrap2svoid_system(writeFile) + wrap1s_system(readFile) systemop getCurrentExceptionMsg registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 3e0e05a94..b5ffd51c2 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -36,6 +36,7 @@ type wColon, wColonColon, wEquals, wDot, wDotDot, wStar, wMinus, wMagic, wThread, wFinal, wProfiler, wObjChecks, + wIntDefine, wStrDefine, wDestroy, @@ -121,7 +122,7 @@ const ":", "::", "=", ".", "..", "*", "-", - "magic", "thread", "final", "profiler", "objchecks", + "magic", "thread", "final", "profiler", "objchecks", "intdefine", "strdefine", "destroy", |