diff options
Diffstat (limited to 'compiler')
48 files changed, 1096 insertions, 716 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index db6003b87..a071060d4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -160,6 +160,7 @@ type nkConstDef, # a const definition nkTypeDef, # a type definition nkYieldStmt, # the yield statement as a tree + nkDefer, # the 'defer' statement nkTryStmt, # a try statement nkFinally, # a finally section nkRaiseStmt, # a raise statement @@ -293,6 +294,7 @@ const # require RC ops sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code + sfExperimental* = sfOverriden # module uses the .experimental switch const # getting ready for the future expr/stmt merge @@ -397,6 +399,7 @@ const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} tyError* = tyProxy # as an errornous node should match everything + tyUnknown* = tyFromExpr tyUnknownTypes* = {tyError, tyFromExpr} @@ -566,8 +569,8 @@ type mBool, mChar, mString, mCstring, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy, - mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, - mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType, + mIsMainModule, mCompileDate, mCompileTime, mProcCall, + mCpuEndian, mHostOS, mHostCPU, mAppType, mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, @@ -610,7 +613,7 @@ const # thus cannot be overloaded (also documented in the spec!): SpecialSemMagics* = { mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, - mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn} + mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr} type PNode* = ref TNode @@ -733,7 +736,7 @@ type # check for the owner when touching 'usedGenerics'. usedGenerics*: seq[PInstantiation] tab*: TStrTable # interface table for modules - of skLet, skVar, skField: + of skLet, skVar, skField, skForVar: guard*: PSym else: nil magic*: TMagic @@ -1055,7 +1058,7 @@ proc discardSons(father: PNode) = father.sons = nil when defined(useNodeIds): - const nodeIdToDebug* = 310841 # 612794 + const nodeIdToDebug* = -1 # 884953 # 612794 #612840 # 612905 # 614635 # 614637 # 614641 # 423408 #429107 # 430443 # 441048 # 441090 # 441153 @@ -1338,6 +1341,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = result = t while result.kind in kinds: result = lastSon(result) +proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType = + result = if t != nil: t.skipTypes(kinds) + else: nil + proc isGCedMem*(t: PType): bool {.inline.} = result = t.kind in {tyString, tyRef, tySequence} or t.kind == tyProc and t.callConv == ccClosure @@ -1515,8 +1522,8 @@ proc getStr*(a: PNode): string = proc getStrOrChar*(a: PNode): string = case a.kind of nkStrLit..nkTripleStrLit: result = a.strVal - of nkCharLit: result = $chr(int(a.intVal)) - else: + of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) + else: internalError(a.info, "getStrOrChar") result = "" @@ -1558,3 +1565,9 @@ 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} +proc makeStmtList*(n: PNode): PNode = + if n.kind == nkStmtList: + result = n + else: + result = newNodeI(nkStmtList, n.info) + result.add n diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 110b0c26e..f23e9a983 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -450,9 +450,10 @@ proc debug(n: PSym) = writeln(stdout, "skUnknown") else: #writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) - writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4, $5", [ - toRope(n.name.s), toRope(n.id), flagsToStr(n.flags), - flagsToStr(n.loc.flags), lineInfoToStr(n.info)]))) + writeln(stdout, "$1_$2: $3, $4, $5, $6" % [ + n.name.s, $n.id, flagsToStr(n.flags).ropeToStr, + flagsToStr(n.loc.flags).ropeToStr, lineInfoToStr(n.info).ropeToStr, + $n.kind]) proc debug(n: PType) = writeln(stdout, ropeToStr(debugType(n))) @@ -638,14 +639,22 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = # This way the newest redefinition is picked by the semantic analyses! assert n.name != nil var h: THash = n.name.h and high(t.data) + var replaceSlot = -1 while true: var it = t.data[h] if it == nil: break + # Semantic checking can happen multiple times thanks to templates + # and overloading: (var x=@[]; x).mapIt(it). + # So it is possible the very same sym is added multiple + # times to the symbol table which we allow here with the 'it == n' check. if it.name.id == n.name.id and reallySameIdent(it.name.s, n.name.s): - t.data[h] = n # overwrite it with newer definition! - return true # found it + if it == n: return false + replaceSlot = h h = nextTry(h, high(t.data)) - if mustRehash(len(t.data), t.counter): + if replaceSlot >= 0: + t.data[replaceSlot] = n # overwrite it with newer definition! + return true # found it + elif mustRehash(len(t.data), t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) else: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b6feb78b2..4a3be0027 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -20,7 +20,7 @@ proc int64Literal(i: BiggestInt): PRope = proc uint64Literal(i: uint64): PRope = toRope($i & "ULL") proc intLiteral(i: BiggestInt): PRope = - if (i > low(int32)) and (i <= high(int32)): + if i > low(int32) and i <= high(int32): result = toRope(i) elif i == low(int32): # Nim has the same bug for the same reasons :-) @@ -39,7 +39,7 @@ proc int32Literal(i: int): PRope = proc genHexLiteral(v: PNode): PRope = # hex literals are unsigned in C # so we don't generate hex literals any longer. - if not (v.kind in {nkIntLit..nkUInt64Lit}): + if v.kind notin {nkIntLit..nkUInt64Lit}: internalError(v.info, "genHexLiteral") result = intLiteral(v.intVal) @@ -224,9 +224,9 @@ proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc = proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let newflags = if src.k == locData: - flags + { needToCopy } + flags + {needToCopy} elif tfShallow in dest.t.flags: - flags - { needToCopy } + flags - {needToCopy} else: flags for i in 0 .. <dest.t.len: @@ -240,9 +240,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, if t == nil: return let newflags = if src.k == locData: - flags + { needToCopy } + flags + {needToCopy} elif tfShallow in dest.t.flags: - flags - { needToCopy } + flags - {needToCopy} else: flags case t.kind @@ -328,7 +328,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyObject: # XXX: check for subtyping? - if needsComplexAssignment(ty): + if not isObjLackingTypeField(ty): + genGenericAsgn(p, dest, src, flags) + elif needsComplexAssignment(ty): if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4: discard getTypeDesc(p.module, ty) internalAssert ty.n != nil @@ -655,13 +657,16 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = ropef(unArithTab[op], [rdLoc(a), toRope(getSize(t) * 8), getSimpleTypeDesc(p.module, e.typ)])) -proc genDeref(p: BProc, e: PNode, d: var TLoc) = - var a: TLoc - if mapType(e.sons[0].typ) in {ctArray, ctPtrToArray}: +proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = + let mt = mapType(e.sons[0].typ) + if mt in {ctArray, ctPtrToArray} and not enforceDeref: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? + #if e[0].kind != nkBracketExpr: + # message(e.info, warnUser, "CAME HERE " & renderTree(e)) expr(p, e.sons[0], d) else: + var a: TLoc initLocExpr(p, e.sons[0], a) case skipTypes(a.t, abstractInst).kind of tyRef: @@ -671,7 +676,15 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = of tyPtr: d.s = OnUnknown # BUGFIX! else: internalError(e.info, "genDeref " & $a.t.kind) - putIntoDest(p, d, a.t.sons[0], ropef("(*$1)", [rdLoc(a)])) + if enforceDeref and mt == ctPtrToArray: + # we lie about the type for better C interop: 'ptr array[3,T]' is + # translated to 'ptr T', but for deref'ing this produces wrong code. + # See tmissingderef. So we get rid of the deref instead. The codegen + # ends up using 'memcpy' for the array assignment, + # so the '&' and '*' cancel out: + putIntoDest(p, d, a.t.sons[0], rdLoc(a)) + else: + putIntoDest(p, d, a.t.sons[0], ropef("(*$1)", [rdLoc(a)])) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: @@ -794,15 +807,15 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = else: genRecordField(p, e.sons[0], d) -proc genArrayElem(p: BProc, e: PNode, d: var TLoc) = +proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) + initLocExpr(p, x, a) + initLocExpr(p, y, b) var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs) var first = intLiteral(firstOrd(ty)) # emit range check: if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags: - if not isConstExpr(e.sons[1]): + if not isConstExpr(y): # semantic pass has already checked for const index expressions if firstOrd(ty) == 0: if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)): @@ -812,26 +825,26 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) = linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n", rdCharLoc(b), first, intLiteral(lastOrd(ty))) else: - let idx = getOrdValue(e.sons[1]) + let idx = getOrdValue(y) if idx < firstOrd(ty) or idx > lastOrd(ty): - localError(e.info, errIndexOutOfBounds) + localError(x.info, errIndexOutOfBounds) d.inheritLocation(a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first)) -proc genCStringElem(p: BProc, e: PNode, d: var TLoc) = +proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) + initLocExpr(p, x, a) + initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange) if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) -proc genOpenArrayElem(p: BProc, e: PNode, d: var TLoc) = +proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) # emit range check: + initLocExpr(p, x, a) + initLocExpr(p, y, b) # emit range check: if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len0)) #raiseIndexError();$n", rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! @@ -839,10 +852,10 @@ proc genOpenArrayElem(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) -proc genSeqElem(p: BProc, e: PNode, d: var TLoc) = +proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) + initLocExpr(p, x, a) + initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: @@ -862,6 +875,17 @@ proc genSeqElem(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b))) +proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = + var ty = skipTypes(n.sons[0].typ, abstractVarRange) + if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) + case ty.kind + of tyArray, tyArrayConstr: genArrayElem(p, n.sons[0], n.sons[1], d) + of tyOpenArray, tyVarargs: genOpenArrayElem(p, n.sons[0], n.sons[1], d) + of tySequence, tyString: genSeqElem(p, n.sons[0], n.sons[1], d) + of tyCString: genCStringElem(p, n.sons[0], n.sons[1], d) + of tyTuple: genTupleElem(p, n, d) + else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # how to generate code? # 'expr1 and expr2' becomes: @@ -903,14 +927,15 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. + internalAssert n.kind == nkBracket discard lists.includeStr(p.module.headerFiles, "<stdio.h>") var args: PRope = nil var a: TLoc - for i in countup(1, n.len-1): + for i in countup(0, n.len-1): initLocExpr(p, n.sons[i], a) appf(args, ", ($1)->data", [rdLoc(a)]) linefmt(p, cpsStmts, "printf($1$2);$n", - makeCString(repeatStr(n.len-1, "%s") & tnl), args) + makeCString(repeatStr(n.len, "%s") & tnl), args) proc gcUsage(n: PNode) = if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) @@ -1111,7 +1136,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = ty = getUniqueType(ty.sons[0]) if field == nil or field.loc.r == nil: internalError(e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: - genFieldCheck(p, it.sons[2], r, field) + genFieldCheck(p, it.sons[2], tmp2.r, field) app(tmp2.r, ".") app(tmp2.r, field.loc.r) tmp2.k = locTemp @@ -1680,7 +1705,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = discard cgsym(p.module, opr.loc.r.ropeToStr) genCall(p, e, d) of mReset: genReset(p, e) - of mEcho: genEcho(p, e) + of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError: localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s) @@ -1702,7 +1727,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = proc genConstExpr(p: BProc, n: PNode): PRope proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = - if (nfAllConst in n.flags) and (d.k == locNone) and (sonsLen(n) > 0): + 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) @@ -1856,18 +1881,34 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = expr(p, n.sons[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) - var src = skipTypes(n.sons[0].typ, abstractPtrs) + + var arg = n.sons[0] + while arg.kind == nkObjDownConv: arg = arg.sons[0] + + var src = skipTypes(arg.typ, abstractPtrs) var a: TLoc - initLocExpr(p, n.sons[0], a) + initLocExpr(p, arg, a) var r = rdLoc(a) - if skipTypes(n.sons[0].typ, abstractInst).kind in {tyRef, tyPtr, tyVar} and - n.sons[0].kind notin {nkHiddenAddr, nkAddr, nkObjDownConv}: + let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar} + if isRef: app(r, "->Sup") - for i in countup(2, abs(inheritanceDiff(dest, src))): app(r, ".Sup") - r = con("&", r) else: - for i in countup(1, abs(inheritanceDiff(dest, src))): app(r, ".Sup") - putIntoDest(p, d, n.typ, r) + app(r, ".Sup") + for i in countup(2, abs(inheritanceDiff(dest, src))): app(r, ".Sup") + if isRef: + # it can happen that we end up generating '&&x->Sup' here, so we pack + # the '&x->Sup' into a temporary and then those address is taken + # (see bug #837). However sometimes using a temporary is not correct: + # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test + # this by ensuring the destination is also a pointer: + if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}: + getTemp(p, n.typ, d) + linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) + else: + r = con("&", r) + putIntoDest(p, d, n.typ, r) + else: + putIntoDest(p, d, n.typ, r) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = var t = getUniqueType(n.typ) @@ -1985,16 +2026,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkCast: genCast(p, n, d) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d) of nkHiddenAddr, nkAddr: genAddr(p, n, d) - of nkBracketExpr: - var ty = skipTypes(n.sons[0].typ, abstractVarRange) - if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) - case ty.kind - of tyArray, tyArrayConstr: genArrayElem(p, n, d) - of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, d) - of tySequence, tyString: genSeqElem(p, n, d) - of tyCString: genCStringElem(p, n, d) - of tyTuple: genTupleElem(p, n, d) - else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + of nkBracketExpr: genBracketExpr(p, n, d) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) of nkDotExpr: genRecordField(p, n, d) of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 0898f0b03..6c7d40d1c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -68,10 +68,21 @@ proc genVarTuple(p: BProc, n: PNode) = [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]) putLocIntoDest(p, v.loc, field) +proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) + proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = if ri.kind in nkCallKinds and (ri.sons[0].kind != nkSym or ri.sons[0].sym.magic == mNone): genAsgnCall(p, le, ri, a) + elif ri.kind in {nkDerefExpr, nkHiddenDeref}: + # this is a hacky way to fix #1181 (tmissingderef):: + # + # var arr1 = cast[ptr array[4, int8]](addr foo)[] + # + # However, fixing this properly really requires modelling 'array' as + # a 'struct' in C to preserve dereferencing semantics completely. Not + # worth the effort until version 1.0 is out. + genDeref(p, ri, a, enforceDeref=true) else: expr(p, ri, a) @@ -787,7 +798,9 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = startBlock(p) var finallyBlock = t.lastSon if finallyBlock.kind == nkFinally: - expr(p, finallyBlock.sons[0], d) + #expr(p, finallyBlock.sons[0], d) + genStmts(p, finallyBlock.sons[0]) + line(p, cpsStmts, ~"throw;$n") endBlock(p) @@ -796,7 +809,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = discard pop(p.nestedTryStmts) if (i < length) and (t.sons[i].kind == nkFinally): - exprBlock(p, t.sons[i].sons[0], d) + genSimpleBlock(p, t.sons[i].sons[0]) proc genTry(p: BProc, t: PNode, d: var TLoc) = # code to generate: @@ -888,7 +901,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = endBlock(p) # end of else block if i < length and t.sons[i].kind == nkFinally: p.finallySafePoints.add(safePoint) - exprBlock(p, t.sons[i].sons[0], d) + genSimpleBlock(p, t.sons[i].sons[0]) discard pop(p.finallySafePoints) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c3905f3e6..460cb9297 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -166,6 +166,10 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope proc needsComplexAssignment(typ: PType): bool = result = containsGarbageCollectedRef(typ) +proc isObjLackingTypeField(typ: PType): bool {.inline.} = + result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and + (typ.sons[0] == nil) or isPureObject(typ)) + proc isInvalidReturnType(rettype: PType): bool = # Arrays and sets cannot be returned by a C procedure, because C is # such a poor programming language. @@ -177,10 +181,12 @@ proc isInvalidReturnType(rettype: PType): bool = of ctArray: result = not (skipTypes(rettype, typedescInst).kind in {tyVar, tyRef, tyPtr}) - of ctStruct: - result = needsComplexAssignment(skipTypes(rettype, typedescInst)) + of ctStruct: + let t = skipTypes(rettype, typedescInst) + result = needsComplexAssignment(t) or + (t.kind == tyObject and not isObjLackingTypeField(t)) else: result = false - + const CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL", "N_STDCALL", "N_CDECL", "N_SAFECALL", @@ -678,10 +684,6 @@ when false: var tmp = getNimType(m) appf(m.s[cfsTypeInit2], "$2 = &$1;$n", [tmp, name]) -proc isObjLackingTypeField(typ: PType): bool {.inline.} = - result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and - (typ.sons[0] == nil) or isPureObject(typ)) - proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = var nimtypeKind: int #allocMemTI(m, typ, name) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 9e353f0ea..c645b81aa 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -55,7 +55,6 @@ proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) = result.s = s result.t = getUniqueType(typ) result.r = nil - #result.a = - 1 result.flags = {} proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = @@ -63,7 +62,6 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = if a.k == locNone: a.k = k a.t = getUniqueType(typ) - #a.a = - 1 a.s = s if a.r == nil: a.r = r @@ -286,7 +284,7 @@ proc genLineDir(p: BProc, t: PNode) = line.toRope, makeCString(toFilename(t.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - (p.prc == nil or sfPure notin p.prc.flags): + (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex >= 0: linefmt(p, cpsStmts, "nimln($1, $2);$n", line.toRope, t.info.quotedFilename) @@ -312,7 +310,7 @@ proc rdLoc(a: TLoc): PRope = proc addrLoc(a: TLoc): PRope = result = a.r if lfIndirect notin a.flags and mapType(a.t) != ctArray: - result = con("&", result) + result = con("(&", result).con(")") proc rdCharLoc(a: TLoc): PRope = # read a location that may need a char-cast: @@ -508,9 +506,7 @@ proc assignLocalVar(p: BProc, s: PSym) = if sfRegister in s.flags: app(decl, " register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # app(decl, " GC_GUARD") - if sfVolatile in s.flags or (p.nestedTryStmts.len > 0 and - not p.module.compileToCpp): - app(decl, " volatile") + if sfVolatile in s.flags: app(decl, " volatile") appf(decl, " $1;$n", [s.loc.r]) else: decl = ropef(s.cgDeclFrmt & ";$n", decl, s.loc.r) diff --git a/compiler/commands.nim b/compiler/commands.nim index f26d1d6c7..a5b0b40d7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -226,6 +226,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation) of "implicitstatic": result = contains(gOptions, optImplicitStatic) of "patterns": result = contains(gOptions, optPatterns) + of "experimental": result = gExperimentalMode else: invalidCmdLineOption(passCmd1, switch, info) proc processPath(path: string, notRelativeToProj = false): string = @@ -568,6 +569,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "none": idents.firstCharIsCS = false else: localError(info, errGenerated, "'partial' or 'none' expected, but found " & arg) + of "experimental": + expectNoArg(switch, arg, pass, info) + gExperimentalMode = true else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 9c07972cf..0472a8731 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -46,7 +46,7 @@ proc countDefinedSymbols*(): int = const additionalSymbols = """ x86 itanium x8664 - msdos mswindows win32 unix posix sunos bsd macintosh RISCOS doslike hpux + msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux mac hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9 diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 35acf1379..3f4f39c27 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -23,6 +23,7 @@ type id: int # for generating IDs toc, section: TSections indexValFilename: string + analytics: string # Google Analytics javascript, "" if doesn't exist seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. PDoc* = ref TDocumentor ## Alias to type less. @@ -61,6 +62,23 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc = initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex), options.gConfigVars, filename, {roSupportRawDirective}, docgenFindFile, compilerMsgHandler) + + if config.hasKey("doc.googleAnalytics"): + result.analytics = """ +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', '$1', 'auto'); + ga('send', 'pageview'); + +</script> + """ % [config["doc.googleAnalytics"]] + else: + result.analytics = "" + result.seenSymbols = newStringTable(modeCaseInsensitive) result.id = 100 @@ -562,10 +580,10 @@ proc genOutFile(d: PDoc): PRope = # XXX what is this hack doing here? 'optCompileOnly' means raw output!? code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", "tableofcontents", "moduledesc", "date", "time", - "content", "author", "version"], + "content", "author", "version", "analytics"], [title.toRope, toc, d.modDesc, toRope(getDateStr()), toRope(getClockStr()), content, d.meta[metaAuthor].toRope, - d.meta[metaVersion].toRope]) + d.meta[metaVersion].toRope, d.analytics.toRope]) else: code = content result = code @@ -630,7 +648,8 @@ proc commandBuildIndex*() = let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", "tableofcontents", "moduledesc", "date", "time", - "content", "author", "version"], + "content", "author", "version", "analytics"], ["Index".toRope, nil, nil, toRope(getDateStr()), - toRope(getClockStr()), content, nil, nil]) + toRope(getClockStr()), content, nil, nil, nil]) + # no analytics because context is not available writeRope(code, getOutFile("theindex", HtmlExt)) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 78cc691c0..ecb898d8a 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -25,16 +25,22 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = if ctx.instLines: result.info = b.info proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = + template handleParam(param) = + let x = param + if x.kind == nkArgList: + for y in items(x): result.add(y) + else: + result.add copyTree(x) + case templ.kind of nkSym: var s = templ.sym if s.owner.id == c.owner.id: - if s.kind == skParam: - let x = actual.sons[s.position] - if x.kind == nkArgList: - for y in items(x): result.add(y) - else: - result.add copyTree(x) + case s.kind + of skParam: + handleParam actual.sons[s.position] + of skGenericParam: + handleParam actual.sons[s.owner.typ.len + s.position - 1] else: internalAssert sfGenSym in s.flags var x = PSym(idTableGet(c.mapping, s)) @@ -56,21 +62,31 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = proc evalTemplateArgs(n: PNode, s: PSym): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar - var a: int - case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - a = sonsLen(n) - else: a = 0 - var f = s.typ.sonsLen - if a > f: globalError(n.info, errWrongNumberOfArguments) + var totalParams = case n.kind + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len + else: 0 + + var + genericParams = s.ast[genericParamsPos].len + expectedRegularParams = <s.typ.len + givenRegularParams = totalParams - genericParams + + if totalParams > expectedRegularParams + genericParams: + globalError(n.info, errWrongNumberOfArguments) result = newNodeI(nkArgList, n.info) - for i in countup(1, f - 1): - var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) - if arg == nil or arg.kind == nkEmpty: + for i in 1 .. givenRegularParams: + result.addSon n.sons[i] + + for i in givenRegularParams+1 .. expectedRegularParams: + let default = s.typ.n.sons[i].sym.ast + if default.kind == nkEmpty: localError(n.info, errWrongNumberOfArguments) - addSon(result, arg) + result.addSon default.copyTree + for i in 1 .. genericParams: + result.addSon n.sons[givenRegularParams + i] + var evalTemplateCounter* = 0 # to prevent endless recursion in templates instantiation diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 4772aecb5..a3423b1d5 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -33,7 +33,7 @@ import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, - intsets, cgmeth + intsets, cgmeth, lowerings type TTarget = enum @@ -113,7 +113,7 @@ proc rdLoc(a: TCompRes): PRope {.inline.} = result = ropef("$1[$2]", a.address, a.res) proc newProc(globals: PGlobals, module: BModule, procDef: PNode, - options: TOptions): PProc = + options: TOptions): PProc = result = PProc( blocks: @[], options: options, @@ -601,15 +601,13 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if p.target == targetJS: app(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if i < length and n.sons[i].kind == nkFinally: - gen(p, n.sons[i].sons[0], a) - moveInto(p, a, r) + genStmt(p, n.sons[i].sons[0]) if p.target == targetJS: app(p.body, "}" & tnl) if p.target == targetLua: # we need to repeat the finally block for Lua ... if i < length and n.sons[i].kind == nkFinally: - gen(p, n.sons[i].sons[0], a) - moveInto(p, a, r) + genStmt(p, n.sons[i].sons[0]) proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -942,22 +940,21 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case s.kind of skVar, skLet, skResult: r.kind = resExpr - if mapType(n.typ) == etyObject: + if mapType(n.sons[0].typ) == etyObject: # make addr() a no-op: r.typ = etyNone r.res = s.loc.r r.address = nil - elif sfGlobal in s.flags: - # globals are always indirect accessible - r.typ = etyBaseIndex - r.address = toRope("Globals") - r.res = makeJSString(ropeToStr(s.loc.r)) - elif sfAddrTaken in s.flags: + elif {sfGlobal, sfAddrTaken} * s.flags != {}: + # for ease of code generation, we do not distinguish between + # sfAddrTaken and sfGlobal. r.typ = etyBaseIndex r.address = s.loc.r r.res = toRope("0") else: - internalError(n.info, "genAddr: 4") + # 'var openArray' for instance produces an 'addr' but this is harmless: + gen(p, n.sons[0], r) + #internalError(n.info, "genAddr: 4 " & renderTree(n)) else: internalError(n.info, "genAddr: 2") of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) @@ -981,7 +978,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = of skVar, skLet, skParam, skTemp, skResult: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) - var k = mapType(s.typ) + let k = mapType(s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: @@ -990,7 +987,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: r.address = s.loc.r r.res = con(s.loc.r, "_Idx") - elif k != etyObject and sfAddrTaken in s.flags: + elif k != etyObject and {sfAddrTaken, sfGlobal} * s.flags != {}: r.res = ropef("$1[0]", [s.loc.r]) else: r.res = s.loc.r @@ -1078,8 +1075,16 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = proc genEcho(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") - app(r.res, "rawEcho") - genArgs(p, n, r) + app(r.res, "rawEcho(") + let n = n[1].skipConv + internalAssert n.kind == nkBracket + for i in countup(0, sonsLen(n) - 1): + let it = n.sons[i] + if it.typ.isCompileTimeOnly: continue + if i > 0: app(r.res, ", ") + genArg(p, it, r) + app(r.res, ")") + r.kind = resExpr proc putToSeq(s: string, indirect: bool): PRope = result = toRope(s) @@ -1162,8 +1167,9 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = internalError("createVar: " & $t.kind) result = nil -proc isIndirect(v: PSym): bool = - result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and +proc isIndirect(v: PSym): bool = + result = {sfAddrTaken, sfGlobal} * v.flags != {} and + (mapType(v.typ) != etyObject) and v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator} proc genVarInit(p: PProc, v: PSym, n: PNode) = @@ -1203,13 +1209,17 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue - assert(a.kind == nkIdentDefs) - assert(a.sons[0].kind == nkSym) - var v = a.sons[0].sym - if lfNoDecl in v.loc.flags: continue - genLineDir(p, a) - genVarInit(p, v, a.sons[2]) + if a.kind != nkCommentStmt: + if a.kind == nkVarTuple: + let unpacked = lowerTupleUnpacking(a, p.prc) + genStmt(p, unpacked) + else: + assert(a.kind == nkIdentDefs) + assert(a.sons[0].kind == nkSym) + var v = a.sons[0].sym + if lfNoDecl notin v.loc.flags: + genLineDir(p, a) + genVarInit(p, v, a.sons[2]) proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): @@ -1425,7 +1435,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = r.res = toRope("{") r.kind = resExpr for i in countup(1, sonsLen(n) - 1): - if i > 0: app(r.res, ", ") + if i > 1: app(r.res, ", ") var it = n.sons[i] internalAssert it.kind == nkExprColonExpr gen(p, it.sons[1], a) @@ -1667,9 +1677,9 @@ proc newModule(module: PSym): BModule = proc genHeader(): PRope = result = ropef("/* Generated by the Nim Compiler v$1 */$n" & "/* (c) 2014 Andreas Rumpf */$n$n" & - "$nvar Globals = this;$n" & "var framePtr = null;$n" & - "var excHandler = null;$n", + "var excHandler = null;$n" & + "var lastJSError = null;$n", [toRope(VersionAsString)]) proc genModule(p: PProc, n: PNode) = diff --git a/compiler/lexer.nim b/compiler/lexer.nim index dbeec8acf..a6b85d7f3 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -35,9 +35,10 @@ type tkSymbol, # keywords: tkAddr, tkAnd, tkAs, tkAsm, tkAtomic, tkBind, tkBlock, tkBreak, tkCase, tkCast, - tkConst, tkContinue, tkConverter, tkDiscard, tkDistinct, tkDiv, tkDo, + tkConst, tkContinue, tkConverter, + tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo, tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport, - tkFinally, tkFor, tkFrom, + tkFinally, tkFor, tkFrom, tkFunc, tkGeneric, tkIf, tkImport, tkIn, tkInclude, tkInterface, tkIs, tkIsnot, tkIterator, tkLet, @@ -71,11 +72,12 @@ const "tkSymbol", "addr", "and", "as", "asm", "atomic", "bind", "block", "break", "case", "cast", - "const", "continue", "converter", "discard", "distinct", "div", "do", + "const", "continue", "converter", + "defer", "discard", "distinct", "div", "do", "elif", "else", "end", "enum", "except", "export", - "finally", "for", "from", "generic", "if", + "finally", "for", "from", "func", "generic", "if", "import", "in", "include", "interface", "is", "isnot", "iterator", - "let", + "let", "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index f9f7d6432..ef43c4d09 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -51,13 +51,14 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = incl(temp.flags, sfFromGeneric) var v = newNodeI(nkVarSection, value.info) - v.addVar(newSymNode(temp)) + let tempAsNode = newSymNode(temp) + v.addVar(tempAsNode) result.add(v) - result.add newAsgnStmt(newSymNode(temp), value) + result.add newAsgnStmt(tempAsNode, value) for i in 0 .. n.len-3: if n.sons[i].kind == nkSym: v.addVar(n.sons[i]) - result.add newAsgnStmt(n.sons[i], newTupleAccess(value, i)) + result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i)) proc createObj*(owner: PSym, info: TLineInfo): PType = result = newType(tyObject, owner) diff --git a/compiler/nim.ini b/compiler/nim.ini index 576b6d2bb..1f14eb21b 100644 --- a/compiler/nim.ini +++ b/compiler/nim.ini @@ -125,7 +125,7 @@ Files: "start.bat" BinPath: r"bin;dist\mingw\bin;dist" ; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry -Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|doc\overview.html" +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"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.1.3.zip|aporia\bin\aporia.exe" ; for now only NSIS supports optional downloads diff --git a/compiler/nim.nimrod.cfg b/compiler/nim.nimrod.cfg index ba7697c4c..f4d8b9dcb 100644 --- a/compiler/nim.nimrod.cfg +++ b/compiler/nim.nimrod.cfg @@ -1,7 +1,5 @@ # Special configuration file for the Nim project -# gc:markAndSweep - hint[XDeclaredButNotUsed]:off path:"llvm" path:"$projectPath/.." diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index 7b42537c4..918f58e3d 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -26,12 +26,13 @@ Options: --styleCheck:on|off|auto performs style checking for identifiers and suggests an alternative spelling; 'auto' corrects the spelling. + --bestEffort try to fix the code even when there + are errors. In addition, all command line options of Nim are supported. """ proc mainCommand = - #msgs.gErrorMax = high(int) # do not stop after first error registerPass verbosePass registerPass semPass gCmd = cmdPretty @@ -63,13 +64,14 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = of "on": gCheckExtern = true of "off": gCheckExtern = false else: localError(gCmdLineInfo, errOnOrOffExpected) - of "stylecheck": + of "stylecheck": case p.val.normalize of "off": gStyleCheck = StyleCheck.None of "on": gStyleCheck = StyleCheck.Warn of "auto": gStyleCheck = StyleCheck.Auto else: localError(gCmdLineInfo, errOnOrOffExpected) of "wholeproject": gOnlyMainfile = false + of "besteffort": msgs.gErrorMax = high(int) # dont stop after first error else: processSwitch(pass, p) of cmdArgument: diff --git a/compiler/nversion.nim b/compiler/nversion.nim index 910ebfb59..8dc4b90b2 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -12,10 +12,6 @@ const MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? - VersionMajor* = 0 - VersionMinor* = 10 - VersionPatch* = 1 - VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch - + VersionAsString* = system.NimVersion RodFileVersion* = "1215" # modify this if the rod-format changes! diff --git a/compiler/options.nim b/compiler/options.nim index 838f99f8e..415ac8430 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -115,6 +115,7 @@ var # the tracked source X, saved by the CAAS client. gDirtyOriginalIdx* = 0'i32 # the original source file of the dirtified buffer. gNoNimblePath* = false + gExperimentalMode*: bool proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc diff --git a/compiler/parser.nim b/compiler/parser.nim index e4de75a07..fab3c453b 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -188,18 +188,13 @@ proc parseTypeDesc(p: var TParser): PNode proc parseDoBlocks(p: var TParser, call: PNode) proc parseParamList(p: var TParser, retColon = true): PNode -proc relevantOprChar(ident: PIdent): char {.inline.} = - result = ident.s[0] - var L = ident.s.len - if result == '\\' and L > 1: - result = ident.s[1] - proc isSigilLike(tok: TToken): bool {.inline.} = - result = tok.tokType == tkOpr and relevantOprChar(tok.ident) == '@' + result = tok.tokType == tkOpr and tok.ident.s[0] == '@' -proc isLeftAssociative(tok: TToken): bool {.inline.} = - ## Determines whether the token is left assocative. - result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^' +proc isRightAssociative(tok: TToken): bool {.inline.} = + ## Determines whether the token is right assocative. + result = tok.tokType == tkOpr and (tok.ident.s[0] == '^' or + (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) proc getPrecedence(tok: TToken, strongSpaces: bool): int = ## Calculates the precedence of the given token. @@ -209,7 +204,10 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int = case tok.tokType of tkOpr: let L = tok.ident.s.len - let relevantChar = relevantOprChar(tok.ident) + let relevantChar = tok.ident.s[0] + + # arrow like? + if L > 1 and tok.ident.s[L-1] == '>': return considerStrongSpaces(1) template considerAsgn(value: expr) = result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value) @@ -269,17 +267,18 @@ proc checkBinary(p: TParser) {.inline.} = #| #| optInd = COMMENT? #| optPar = (IND{>} | IND{=})? -#| -#| simpleExpr = assignExpr (OP0 optInd assignExpr)* -#| assignExpr = orExpr (OP1 optInd orExpr)* -#| orExpr = andExpr (OP2 optInd andExpr)* -#| andExpr = cmpExpr (OP3 optInd cmpExpr)* -#| cmpExpr = sliceExpr (OP4 optInd sliceExpr)* -#| sliceExpr = ampExpr (OP5 optInd ampExpr)* -#| ampExpr = plusExpr (OP6 optInd plusExpr)* -#| plusExpr = mulExpr (OP7 optInd mulExpr)* -#| mulExpr = dollarExpr (OP8 optInd dollarExpr)* -#| dollarExpr = primary (OP9 optInd primary)* +#| +#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* +#| arrowExpr = assignExpr (OP1 optInd assignExpr)* +#| assignExpr = orExpr (OP2 optInd orExpr)* +#| orExpr = andExpr (OP3 optInd andExpr)* +#| andExpr = cmpExpr (OP4 optInd cmpExpr)* +#| cmpExpr = sliceExpr (OP5 optInd sliceExpr)* +#| sliceExpr = ampExpr (OP6 optInd ampExpr)* +#| ampExpr = plusExpr (OP7 optInd plusExpr)* +#| plusExpr = mulExpr (OP8 optInd mulExpr)* +#| mulExpr = dollarExpr (OP9 optInd dollarExpr)* +#| dollarExpr = primary (OP10 optInd primary)* proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) @@ -506,7 +505,7 @@ proc parsePar(p: var TParser): PNode = getTok(p) optInd(p, result) if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, - tkTry, tkFinally, tkExcept, tkFor, tkBlock, + tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock, tkConst, tkLet, tkWhen, tkVar, tkMixin}: # XXX 'bind' used to be an expression, so we exclude it here; @@ -734,7 +733,7 @@ proc parseOperators(p: var TParser, headNode: PNode, # the operator itself must not start on a new line: while opPrec >= limit and p.tok.indent < 0 and not isUnary(p): checkBinary(p) - var leftAssoc = ord(isLeftAssociative(p.tok)) + var leftAssoc = 1-ord(isRightAssociative(p.tok)) var a = newNodeP(nkInfix, p) var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: getTok(p) @@ -895,7 +894,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode = var a: PNode result = newNodeP(nkFormalParams, p) addSon(result, ast.emptyNode) # return type - if p.tok.tokType == tkParLe and p.tok.indent < 0: + let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 + if hasParLe: getTok(p) optInd(p, result) while true: @@ -919,6 +919,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode = getTok(p) optInd(p, result) result.sons[0] = parseTypeDesc(p) + elif not retColon and not hasParle: + # Mark as "not there" in order to mark for deprecation in the semantic pass: + result = ast.emptyNode proc optPragmas(p: var TParser): PNode = if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): @@ -1131,7 +1134,7 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode = skipComment(p, result) if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: let body = parseStmt(p) - addSon(result, newProcNode(nkDo, body.info, body)) + addSon(result, makeStmtList(body)) while sameInd(p): var b: PNode case p.tok.tokType @@ -1423,9 +1426,10 @@ proc parseBlock(p: var TParser): PNode = colcom(p, result) addSon(result, parseStmt(p)) -proc parseStatic(p: var TParser): PNode = +proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode = #| staticStmt = 'static' colcom stmt - result = newNodeP(nkStaticStmt, p) + #| deferStmt = 'defer' colcom stmt + result = newNodeP(k, p) getTokNoInd(p) colcom(p, result) addSon(result, parseStmt(p)) @@ -1863,7 +1867,7 @@ proc simpleStmt(p: var TParser): PNode = proc complexOrSimpleStmt(p: var TParser): PNode = #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt #| | tryStmt | finallyStmt | exceptStmt | forStmt - #| | blockStmt | staticStmt | asmStmt + #| | blockStmt | staticStmt | deferStmt | asmStmt #| | 'proc' routine #| | 'method' routine #| | 'iterator' routine @@ -1884,7 +1888,8 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkExcept: result = parseExceptBlock(p, nkExceptBranch) of tkFor: result = parseFor(p) of tkBlock: result = parseBlock(p) - of tkStatic: result = parseStatic(p) + of tkStatic: result = parseStaticOrDefer(p, nkStaticStmt) + of tkDefer: result = parseStaticOrDefer(p, nkDefer) of tkAsm: result = parseAsm(p) of tkProc: result = parseRoutine(p, nkProcDef) of tkMethod: result = parseRoutine(p, nkMethodDef) @@ -1979,15 +1984,17 @@ proc parseTopLevelStmt(p: var TParser): PNode = ## top-level statement or emptyNode if end of stream. result = ast.emptyNode while true: - if p.tok.indent != 0: + if p.tok.indent != 0: if p.firstTok and p.tok.indent < 0: discard - else: parMessage(p, errInvalidIndentation) + elif p.tok.tokType != tkSemiColon: + parMessage(p, errInvalidIndentation) p.firstTok = false case p.tok.tokType of tkSemiColon: getTok(p) if p.tok.indent <= 0: discard else: parMessage(p, errInvalidIndentation) + p.firstTok = true of tkEof: break else: result = complexOrSimpleStmt(p) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 3d71aa4d1..9ac0988c5 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -260,7 +260,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = # couldn't bind parameter: if isNil(x): return nil result.add(x) - if requiresAA: addToArgList(args, n) + if requiresAA: addToArgList(args, x) # perform alias analysis here: if requiresAA: for i in 1 .. < params.len: diff --git a/compiler/platform.nim b/compiler/platform.nim index 2e78d4fc5..c6cb3d244 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b548837fe..8e639f083 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -45,7 +45,7 @@ const wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto, - wInjectStmt, wDeprecated} + wInjectStmt, wDeprecated, wExperimental} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, @@ -357,11 +357,11 @@ proc processPush(c: PContext, n: PNode, start: int) = append(c.optionStack, x) for i in countup(start, sonsLen(n) - 1): if processOption(c, n.sons[i]): - # simply store it somehwere: + # simply store it somewhere: if x.otherPragmas.isNil: x.otherPragmas = newNodeI(nkPragma, n.info) x.otherPragmas.add n.sons[i] - #LocalError(n.info, errOptionExpected) + #localError(n.info, errOptionExpected) proc processPop(c: PContext, n: PNode) = if c.optionStack.counter <= 1: @@ -850,6 +850,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, localError(it.info, errExprExpected) else: it.sons[1] = c.semExpr(c, it.sons[1]) + of wExperimental: + noVal(it) + if isTopLevel(c): + c.module.flags.incl sfExperimental + else: + localError(it.info, "'experimental' pragma only valid as toplevel statement") else: invalidPragma(it) else: invalidPragma(it) else: processNote(c, it) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index a4469acda..2bbe8ac80 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -34,6 +34,7 @@ type comStack*: seq[PNode] # comment stack flags*: TRenderFlags checkAnon: bool # we're in a context that can contain sfAnon + inPragma: int proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {}) @@ -620,7 +621,7 @@ proc gpattern(g: var TSrcGen, n: PNode) = if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments - gstmts(g, n.sons[0], c) + gstmts(g, n, c) put(g, tkCurlyRi, "}") proc gpragmaBlock(g: var TSrcGen, n: PNode) = @@ -894,8 +895,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") for i in countup(0, sonsLen(n) - 1): if i > 0: put(g, tkOpr, "|") - gsub(g, n.sons[i], c) - put(g, tkParRi, ")") + if n.sons[i].kind == nkSym: + let s = n[i].sym + if s.owner != nil: + put g, tkSymbol, n[i].sym.owner.name.s + put g, tkOpr, "." + put g, tkSymbol, n[i].sym.name.s + else: + gsub(g, n.sons[i], c) + put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")") of nkPar, nkClosure: put(g, tkParLe, "(") gcomma(g, n, c) @@ -940,10 +948,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkConstDef, nkIdentDefs: gcomma(g, n, 0, -3) var L = sonsLen(n) - if n.sons[L - 2].kind != nkEmpty: + if L >= 2 and n.sons[L - 2].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n.sons[L - 2]) - if n.sons[L - 1].kind != nkEmpty: + if L >= 1 and n.sons[L - 1].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n.sons[L - 1], c) @@ -1177,12 +1185,17 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkContinueStmt: putWithSpace(g, tkContinue, "continue") gsub(g, n.sons[0]) - of nkPragma: + of nkPragma: if renderNoPragmas notin g.flags: - put(g, tkSpaces, Space) - put(g, tkCurlyDotLe, "{.") - gcomma(g, n, emptyContext) - put(g, tkCurlyDotRi, ".}") + if g.inPragma <= 0: + inc g.inPragma + put(g, tkSpaces, Space) + put(g, tkCurlyDotLe, "{.") + gcomma(g, n, emptyContext) + put(g, tkCurlyDotRi, ".}") + dec g.inPragma + else: + gcomma(g, n, emptyContext) of nkImportStmt, nkExportStmt: if n.kind == nkImportStmt: putWithSpace(g, tkImport, "import") diff --git a/compiler/sem.nim b/compiler/sem.nim index 81846e1b4..9ac7ad139 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -41,7 +41,6 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind) proc maybeAddResult(c: PContext, s: PSym, n: PNode) proc instGenericContainer(c: PContext, n: PNode, header: PType): PType proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode -proc fixImmediateParams(n: PNode): PNode proc activate(c: PContext, n: PNode) proc semQuoteAst(c: PContext, n: PNode): PNode proc finishMethod(c: PContext, s: PSym) @@ -68,12 +67,25 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = # error correction: result = copyTree(arg) result.typ = formal + else: + let x = result.skipConv + if x.kind == nkPar and formal.kind != tyExpr: + changeType(x, formal, check=true) proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode var commonTypeBegin = PType(kind: tyExpr) +proc isEmptyContainer(t: PType): bool = + case t.kind + of tyExpr, tyNil: result = true + of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty + of tySet, tySequence, tyOpenArray, tyVarargs: + result = t.sons[0].kind == tyEmpty + of tyGenericInst: result = isEmptyContainer(t.lastSon) + else: result = false + proc commonType*(x, y: PType): PType = # new type relation that is used for array constructors, # if expressions, etc.: @@ -97,6 +109,13 @@ proc commonType*(x, y: PType): PType = # check for seq[empty] vs. seq[int] let idx = ord(b.kind in {tyArray, tyArrayConstr}) if a.sons[idx].kind == tyEmpty: return y + elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len: + var nt: PType + for i in 0.. <a.len: + if isEmptyContainer(a.sons[i]) and not isEmptyContainer(b.sons[i]): + if nt.isNil: nt = copyType(a, a.owner, false) + nt.sons[i] = b.sons[i] + if not nt.isNil: result = nt #elif b.sons[idx].kind == tyEmpty: return x elif a.kind == tyRange and b.kind == tyRange: # consider: (range[0..3], range[0..4]) here. We should make that @@ -133,17 +152,14 @@ proc commonType*(x, y: PType): PType = result = newType(k, r.owner) result.addSonSkipIntLit(r) -proc isTopLevel(c: PContext): bool {.inline.} = - result = c.currentScope.depthLevel <= 2 - 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 = # like newSymS, but considers gensym'ed symbols if n.kind == nkSym: + # and sfGenSym in n.sym.flags: result = n.sym - internalAssert sfGenSym in result.flags internalAssert result.kind == kind # when there is a nested proc inside a template, semtmpl # will assign a wrong owner during the first pass over the diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 3971b8ff5..961c61c57 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -277,16 +277,27 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = styleCheckUse(n.sons[0].info, finalCallee) if finalCallee.ast == nil: internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check! - if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: - # a generic proc! - if not x.proxyMatch: + if x.hasFauxMatch: + result = x.call + result.sons[0] = newSymNode(finalCallee, result.sons[0].info) + if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: + result.typ = newTypeS(x.fauxMatch, c) + return + let gp = finalCallee.ast.sons[genericParamsPos] + if gp.kind != nkEmpty: + if x.calleeSym.kind notin {skMacro, skTemplate}: finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) else: - result = x.call - result.sons[0] = newSymNode(finalCallee, result.sons[0].info) - result.typ = finalCallee.typ.sons[0] - if containsGenericType(result.typ): result.typ = errorType(c) - return + # For macros and templates, the resolved generic params + # 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 result = x.call instGenericConvertersSons(c, result, x) result.sons[0] = newSymNode(finalCallee, result.sons[0].info) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 921e87d30..623f9b633 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -268,7 +268,7 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = template rangeHasStaticIf*(t: PType): bool = # this accepts the ranges's node - t.n[1].kind == nkStaticExpr + t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr template getStaticTypeFromRange*(t: PType): PType = t.n[1][0][1].typ @@ -312,3 +312,8 @@ proc checkSonsLen*(n: PNode, length: int) = proc checkMinSonsLen*(n: PNode, length: int) = if sonsLen(n) < length: illFormedAst(n) +proc isTopLevel*(c: PContext): bool {.inline.} = + result = c.currentScope.depthLevel <= 2 + +proc experimentalMode*(c: PContext): bool {.inline.} = + result = gExperimentalMode or sfExperimental in c.module.flags diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index bf2344f11..68921a15a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -219,7 +219,7 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = # gnrc. params, then it won't be necessary to open a new scope here openScope(c) var lifted = liftParamType(c, skType, newNodeI(nkArgList, info), - t, ":anon", info) + t, ":anon", info) closeScope(c) if lifted != nil: t = lifted @@ -229,7 +229,7 @@ proc semConv(c: PContext, n: PNode): PNode = return n result = newNodeI(nkConv, n.info) - var targetType = semTypeNode(c, n.sons[0], nil) + var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) maybeLiftType(targetType, c, n[0].info) result.addSon copyTree(n.sons[0]) var op = semExprWithType(c, n.sons[1]) @@ -423,34 +423,22 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i]) result = semExpr(c, result) -proc changeType(n: PNode, newType: PType, check: bool) = +proc changeType(n: PNode, newType: PType, check: bool) = case n.kind - of nkCurly, nkBracket: - for i in countup(0, sonsLen(n) - 1): + of nkCurly, nkBracket: + for i in countup(0, sonsLen(n) - 1): changeType(n.sons[i], elemType(newType), check) - of nkPar: - if newType.kind != tyTuple: + of nkPar: + let tup = newType.skipTypes({tyGenericInst}) + if tup.kind != tyTuple: internalError(n.info, "changeType: no tuple type for constructor") - elif newType.n == nil: discard - elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: - for i in countup(0, sonsLen(n) - 1): - var m = n.sons[i].sons[0] - if m.kind != nkSym: - internalError(m.info, "changeType(): invalid tuple constr") - return - var f = getSymFromList(newType.n, m.sym.name) - if f == nil: - internalError(m.info, "changeType(): invalid identifier") - return - changeType(n.sons[i].sons[1], f.typ, check) else: for i in countup(0, sonsLen(n) - 1): var m = n.sons[i] - var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) - addSon(a, newSymNode(newType.n.sons[i].sym)) - addSon(a, m) - changeType(m, newType.sons[i], check) - n.sons[i] = a + if m.kind == nkExprColonExpr: + m = m.sons[1] + n.sons[i] = m + changeType(m, tup.sons[i], check) of nkCharLit..nkUInt64Lit: if check: let value = n.intVal @@ -541,7 +529,8 @@ proc fixAbstractType(c: PContext, n: PNode) = elif skipTypes(it.sons[1].typ, abstractVar).kind in {tyNil, tyArrayConstr, tyTuple, tySet}: var s = skipTypes(it.typ, abstractVar) - changeType(it.sons[1], s, check=true) + if s.kind != tyExpr: + changeType(it.sons[1], s, check=true) n.sons[i] = it.sons[1] of nkBracket: # an implicitly constructed array (passed to an open array): @@ -729,7 +718,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, case callee.kind of skMacro, skTemplate: discard else: - if (callee.kind in skIterators) and (callee.id == c.p.owner.id): + if callee.kind in skIterators and callee.id == c.p.owner.id: localError(n.info, errRecursiveDependencyX, callee.name.s) if sfNoSideEffect notin callee.flags: if {sfImportc, sfSideEffect} * callee.flags != {}: @@ -791,7 +780,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) elif t != nil and t.kind == tyTypeDesc: if n.len == 1: return semObjConstr(c, n, flags) - let destType = t.skipTypes({tyTypeDesc, tyGenericInst}) return semConv(c, n) else: result = overloadedCallOpr(c, n) @@ -840,45 +828,17 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) -proc buildStringify(c: PContext, arg: PNode): PNode = - if arg.typ != nil and - skipTypes(arg.typ, abstractInst-{tyTypeDesc}).kind == tyString: - result = arg - else: - result = newNodeI(nkCall, arg.info) - addSon(result, newIdentNode(getIdent"$", arg.info)) - addSon(result, arg) - -proc semEcho(c: PContext, n: PNode): PNode = - # this really is a macro - checkMinSonsLen(n, 1) - for i in countup(1, sonsLen(n) - 1): - var arg = semExprWithType(c, n.sons[i]) - arg = semExprWithType(c, buildStringify(c, arg)) - n.sons[i] = arg - let t = arg.typ - if (t == nil or t.skipTypes(abstractInst).kind != tyString) and - arg.kind != nkEmpty: - localError(n.info, errGenerated, - "implicitly invoked '$' does not return string") - let t = n.sons[0].typ - if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) - result = n - proc buildEchoStmt(c: PContext, n: PNode): PNode = - # we MUST not check 'n' for semantics again here! + # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) var e = strTableGet(magicsys.systemModule.tab, getIdent"echo") if e != nil: - addSon(result, newSymNode(e)) + add(result, newSymNode(e)) else: localError(n.info, errSystemNeeds, "echo") - addSon(result, errorNode(c, n)) - var arg = buildStringify(c, n) - # problem is: implicit '$' is not checked for semantics yet. So we give up - # and check 'arg' for semantics again: - arg = semExpr(c, arg) - if arg != nil: addSon(result, arg) + add(result, errorNode(c, n)) + add(result, n) + result = semExpr(c, result) proc semExprNoType(c: PContext, n: PNode): PNode = result = semExpr(c, n, {efWantStmt}) @@ -965,18 +925,19 @@ const proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias - else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1]) - + else: (internalAssert(typ.kind == tyCompositeTypeClass); + typ.sons[1].skipGenericAlias) let tbody = ty.sons[0] for s in countup(0, tbody.len-2): let tParam = tbody.sons[s] - if tParam.sym.name == paramName: + if tParam.sym.name.id == paramName.id: let rawTyp = ty.sons[s + 1] if rawTyp.kind == tyStatic: return rawTyp.n else: let foundTyp = makeTypeDesc(c, rawTyp) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) + #echo "came here: returned nil" proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if it's not a built-in field access @@ -988,9 +949,14 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if s != nil: - markUsed(n.sons[1].info, s) + if s.kind in OverloadableSyms: + result = symChoice(c, n, s, scClosed) + if result.kind == nkSym: result = semSym(c, n, s, flags) + else: + markUsed(n.sons[1].info, s) + result = semSym(c, n, s, flags) styleCheckUse(n.sons[1].info, s) - return semSym(c, n, s, flags) + return n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) #restoreOldStyleType(n.sons[0]) @@ -998,6 +964,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var ty = n.sons[0].typ var f: PSym = nil result = nil + if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): if ty.kind == tyTypeDesc: ty = ty.base ty = ty.skipTypes(tyDotOpTransparent) @@ -1006,7 +973,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # look up if the identifier belongs to the enum: while ty != nil: f = getSymFromList(ty.n, i) - if f != nil: break + if f != nil: break ty = ty.sons[0] # enum inheritance if f != nil: result = newSymNode(f) @@ -1018,7 +985,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = of tyTypeParamsHolders: return readTypeParameter(c, ty, i, n.info) of tyObject, tyTuple: - if ty.n.kind == nkRecList: + if ty.n != nil and ty.n.kind == nkRecList: for field in ty.n: if field.sym.name == i: n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ]) @@ -1272,8 +1239,8 @@ proc semAsgn(c: PContext, n: PNode): PNode = proc semReturn(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) - if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or - c.p.owner.kind == skClosureIterator: + if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro, + skClosureIterator}: if n.sons[0].kind != nkEmpty: # transform ``return expr`` to ``result = expr; return`` if c.p.resultSym != nil: @@ -1456,6 +1423,8 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo, proc semUsing(c: PContext, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) + if not experimentalMode(c): + localError(n.info, "use the {.experimental.} pragma to enable 'using'") for e in n.sons: let usedSym = semExpr(c, e) if usedSym.kind == nkSym: @@ -1538,11 +1507,11 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = doBlk.sons[namePos] = newAnonSym(skTemplate, n.info).newSymNode if ids.len > 0: - doBlk[paramsPos].sons.setLen(2) - doBlk[paramsPos].sons[0] = getSysSym("stmt").newSymNode # return type + doBlk.sons[paramsPos] = newNodeI(nkFormalParams, n.info) + doBlk[paramsPos].add getSysSym("stmt").newSymNode # return type ids.add getSysSym("expr").newSymNode # params type ids.add emptyNode # no default value - doBlk[paramsPos].sons[1] = newNode(nkIdentDefs, n.info, ids) + doBlk[paramsPos].add newNode(nkIdentDefs, n.info, ids) var tmpl = semTemplateDef(c, doBlk) quotes[0] = tmpl[namePos] @@ -1650,7 +1619,6 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mSizeOf: result = semSizeof(c, setMs(n, s)) of mIs: result = semIs(c, setMs(n, s)) of mOf: result = semOf(c, setMs(n, s)) - of mEcho: result = semEcho(c, setMs(n, s)) of mShallowCopy: result = semShallowCopy(c, n, flags) of mExpandToAst: result = semExpandToAst(c, n, s, flags) of mQuoteAst: result = semQuoteAst(c, n) @@ -1674,6 +1642,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = else: result.typ = result[1].typ result.add instantiateCreateFlowVarCall(c, result[1].typ, n.info).newSymNode + of mProcCall: + result = setMs(n, s) + result.sons[1] = semExpr(c, n.sons[1]) + result.typ = n[1].typ else: result = semDirectOp(c, n, flags) proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = @@ -1922,19 +1894,6 @@ proc semBlock(c: PContext, n: PNode): PNode = closeScope(c) dec(c.p.nestedBlockCounter) -proc doBlockIsStmtList(n: PNode): bool = - result = n.kind == nkDo and - n[paramsPos].sonsLen == 1 and - n[paramsPos][0].kind == nkEmpty - -proc fixImmediateParams(n: PNode): PNode = - # XXX: Temporary work-around until we carry out - # the planned overload resolution reforms - for i in 1 .. <safeLen(n): - if doBlockIsStmtList(n[i]): - n.sons[i] = n[i][bodyPos] - result = n - proc semExport(c: PContext, n: PNode): PNode = var x = newNodeI(n.kind, n.info) #let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len @@ -2052,14 +2011,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if sfImmediate notin s.flags: result = semDirectOp(c, n, flags) else: - var p = fixImmediateParams(n) - result = semMacroExpr(c, p, p, s, flags) + result = semMacroExpr(c, n, n, s, flags) of skTemplate: if sfImmediate notin s.flags: result = semDirectOp(c, n, flags) else: - var p = fixImmediateParams(n) - result = semTemplateExpr(c, p, s, flags) + result = semTemplateExpr(c, n, s, flags) of skType: # XXX think about this more (``set`` procs) if n.len == 2: diff --git a/compiler/semfields.nim b/compiler/semfields.nim new file mode 100644 index 000000000..a6ade64d5 --- /dev/null +++ b/compiler/semfields.nim @@ -0,0 +1,163 @@ +# +# +# The Nim Compiler +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module does the semantic transformation of the fields* iterators. +# included from semstmts.nim + +type + TFieldInstCtx = object # either 'tup[i]' or 'field' is valid + tupleType: PType # if != nil we're traversing a tuple + tupleIndex: int + field: PSym + replaceByFieldName: bool + +proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = + case n.kind + of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n + of nkIdent: + result = n + var L = sonsLen(forLoop) + if c.replaceByFieldName: + if n.ident.id == forLoop[0].ident.id: + let fieldName = if c.tupleType.isNil: c.field.name.s + elif c.tupleType.n.isNil: "Field" & $c.tupleIndex + else: c.tupleType.n.sons[c.tupleIndex].sym.name.s + result = newStrNode(nkStrLit, fieldName) + return + # other fields: + for i in ord(c.replaceByFieldName)..L-3: + if n.ident.id == forLoop[i].ident.id: + var call = forLoop.sons[L-2] + var tupl = call.sons[i+1-ord(c.replaceByFieldName)] + if c.field.isNil: + result = newNodeI(nkBracketExpr, n.info) + result.add(tupl) + result.add(newIntNode(nkIntLit, c.tupleIndex)) + else: + result = newNodeI(nkDotExpr, n.info) + result.add(tupl) + result.add(newSymNode(c.field, n.info)) + break + else: + if n.kind == nkContinueStmt: + localError(n.info, errGenerated, + "'continue' not supported in a 'fields' loop") + result = copyNode(n) + newSons(result, sonsLen(n)) + for i in countup(0, sonsLen(n)-1): + result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop) + +type + TFieldsCtx = object + c: PContext + m: TMagic + +proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = + case typ.kind + of nkSym: + var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid + fc.field = typ.sym + fc.replaceByFieldName = c.m == mFieldPairs + openScope(c.c) + inc c.c.inUnrolledContext + let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) + father.add(semStmt(c.c, body)) + dec c.c.inUnrolledContext + closeScope(c.c) + of nkNilLit: discard + of nkRecCase: + let L = forLoop.len + let call = forLoop.sons[L-2] + if call.len > 2: + localError(forLoop.info, errGenerated, + "parallel 'fields' iterator does not work for 'case' objects") + return + # iterate over the selector: + semForObjectFields(c, typ[0], forLoop, father) + # we need to generate a case statement: + var caseStmt = newNodeI(nkCaseStmt, forLoop.info) + # generate selector: + var access = newNodeI(nkDotExpr, forLoop.info, 2) + access.sons[0] = call.sons[1] + access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info) + caseStmt.add(semExprWithType(c.c, access)) + # copy the branches over, but replace the fields with the for loop body: + for i in 1 .. <typ.len: + var branch = copyTree(typ[i]) + let L = branch.len + branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info) + semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1]) + caseStmt.add(branch) + father.add(caseStmt) + of nkRecList: + for t in items(typ): semForObjectFields(c, t, forLoop, father) + else: + illFormedAst(typ) + +proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = + # so that 'break' etc. work as expected, we produce + # a 'while true: stmt; break' loop ... + result = newNodeI(nkWhileStmt, n.info, 2) + var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true") + if trueSymbol == nil: + localError(n.info, errSystemNeeds, "true") + trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info) + trueSymbol.typ = getSysType(tyBool) + + result.sons[0] = newSymNode(trueSymbol, n.info) + var stmts = newNodeI(nkStmtList, n.info) + result.sons[1] = stmts + + var length = sonsLen(n) + var call = n.sons[length-2] + if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs): + localError(n.info, errWrongNumberOfVariables) + return result + + var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc}) + if tupleTypeA.kind notin {tyTuple, tyObject}: + localError(n.info, errGenerated, "no object or tuple type") + return result + for i in 1..call.len-1: + var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc}) + if not sameType(tupleTypeA, tupleTypeB): + typeMismatch(call.sons[i], tupleTypeA, tupleTypeB) + + inc(c.p.nestedLoopCounter) + if tupleTypeA.kind == tyTuple: + var loopBody = n.sons[length-1] + for i in 0..sonsLen(tupleTypeA)-1: + openScope(c) + var fc: TFieldInstCtx + fc.tupleType = tupleTypeA + fc.tupleIndex = i + fc.replaceByFieldName = m == mFieldPairs + var body = instFieldLoopBody(fc, loopBody, n) + inc c.inUnrolledContext + stmts.add(semStmt(c, body)) + dec c.inUnrolledContext + closeScope(c) + else: + var fc: TFieldsCtx + fc.m = m + fc.c = c + var t = tupleTypeA + while t.kind == tyObject: + semForObjectFields(fc, t.n, n, stmts) + if t.sons[0] == nil: break + t = skipTypes(t.sons[0], abstractPtrs) + dec(c.p.nestedLoopCounter) + # for TR macros this 'while true: ...; break' loop is pretty bad, so + # we avoid it now if we can: + if hasSonWith(stmts, nkBreakStmt): + var b = newNodeI(nkBreakStmt, n.info) + b.add(ast.emptyNode) + stmts.add(b) + else: + result = stmts diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 76ac23e0d..220abcad7 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -197,18 +197,20 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = of mSubI, mSubI64, mSubU: binaryOp(`|-|`) of mBitandI, mBitandI64: + # since uint64 is still not even valid for 'range' (since it's no ordinal + # yet), we exclude it from the list (see bug #1638) for now: var a = n.sons[1] var b = n.sons[2] # symmetrical: - if b.kind notin {nkIntLit..nkUInt64Lit}: swap(a, b) - if b.kind in {nkIntLit..nkUInt64Lit}: + if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b) + if b.kind in {nkIntLit..nkUInt32Lit}: let x = b.intVal|+|1 if (x and -x) == x and x >= 0: result = makeRange(a.typ, 0, b.intVal) of mModU: let a = n.sons[1] let b = n.sons[2] - if b.kind in {nkIntLit..nkUInt64Lit}: + if b.kind in {nkIntLit..nkUInt32Lit}: if b.intVal >= 0: result = makeRange(a.typ, 0, b.intVal-1) else: @@ -319,8 +321,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of tyInt64, tyInt, tyUInt..tyUInt64: result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) else: internalError(n.info, "constant folding for shr") - of mDivI, mDivI64: result = newIntNodeT(getInt(a) div getInt(b), n) - of mModI, mModI64: result = newIntNodeT(getInt(a) mod getInt(b), n) + of mDivI, mDivI64: + let y = getInt(b) + if y != 0: + result = newIntNodeT(getInt(a) div y, n) + of mModI, mModI64: + let y = getInt(b) + if y != 0: + result = newIntNodeT(getInt(a) mod y, n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) @@ -359,8 +367,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) - of mModU: result = newIntNodeT(`%%`(getInt(a), getInt(b)), n) - of mDivU: result = newIntNodeT(`/%`(getInt(a), getInt(b)), n) + of mModU: + let y = getInt(b) + if y != 0: + result = newIntNodeT(`%%`(getInt(a), y), n) + of mDivU: + let y = getInt(b) + if y != 0: + result = newIntNodeT(`/%`(getInt(a), y), n) of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n) of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n) of mLtSet: @@ -502,7 +516,7 @@ proc foldConv*(n, a: PNode; check = false): PNode = of tyInt..tyInt64: case skipTypes(a.typ, abstractRange).kind of tyFloat..tyFloat64: - result = newIntNodeT(system.toInt(getFloat(a)), n) + result = newIntNodeT(int(getFloat(a)), n) of tyChar: result = newIntNodeT(getOrdValue(a), n) else: result = a @@ -601,10 +615,6 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) of mCompileDate: result = newStrNodeT(times.getDateStr(), n) of mCompileTime: result = newStrNodeT(times.getClockStr(), n) - of mNimrodVersion: result = newStrNodeT(VersionAsString, n) - of mNimrodMajor: result = newIntNodeT(VersionMajor, n) - of mNimrodMinor: result = newIntNodeT(VersionMinor, n) - of mNimrodPatch: result = newIntNodeT(VersionPatch, 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) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 6c218fa0c..1ab4f5989 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -13,7 +13,7 @@ # A problem is that it cannot be detected if the symbol is introduced # as in ``var x = ...`` or used because macros/templates can hide this! # So we have to eval templates/macros right here so that symbol -# lookup can be accurate. XXX But this can only be done for immediate macros! +# lookup can be accurate. # included from sem.nim @@ -48,7 +48,6 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skTemplate: if macroToExpand(s): styleCheckUse(n.info, s) - let n = fixImmediateParams(n) result = semTemplateExpr(c, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: @@ -61,13 +60,20 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, else: result = symChoice(c, n, s, scOpen) of skGenericParam: - result = newSymNodeTypeDesc(s, n.info) + if s.typ != nil and s.typ.kind == tyStatic: + if s.typ.n != nil: + result = s.typ.n + else: + result = n + else: + result = newSymNodeTypeDesc(s, n.info) styleCheckUse(n.info, s) of skParam: result = n styleCheckUse(n.info, s) of skType: - if (s.typ != nil) and (s.typ.kind != tyGenericParam): + if (s.typ != nil) and + (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): result = newSymNodeTypeDesc(s, n.info) else: result = n @@ -99,7 +105,7 @@ proc newDot(n, b: PNode): PNode = result.add(b) proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var IntSet): PNode = + ctx: var IntSet; isMacro: var bool): PNode = assert n.kind == nkDotExpr let luf = if withinMixin notin flags: {checkUndeclared} else: {} @@ -113,6 +119,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, let ident = considerQuotedIdent(n) var s = searchInScopes(c, ident).skipAlias(n) if s != nil and s.kind in routineKinds: + isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags: result = newDot(result, symChoice(c, n, s, scClosed)) elif s.name.id in ctx: @@ -141,7 +148,8 @@ proc semGenericStmt(c: PContext, n: PNode, #var s = qualifiedLookUp(c, n, luf) #if s != nil: result = semGenericStmtSymbol(c, n, s) # XXX for example: ``result.add`` -- ``add`` needs to be looked up here... - result = fuzzyLookup(c, n, flags, ctx) + var dummy: bool + result = fuzzyLookup(c, n, flags, ctx, dummy) of nkEmpty, nkSym..nkNilLit: # see tests/compile/tgensymgeneric.nim: # We need to open the gensym'ed symbol again so that the instantiation @@ -165,10 +173,10 @@ proc semGenericStmt(c: PContext, n: PNode, localError(n.info, errUndeclaredIdentifier, fn.renderTree) var first = 0 - var isDefinedMagic = false - if s != nil: + var mixinContext = false + if s != nil: incl(s.flags, sfUsed) - isDefinedMagic = s.magic in {mDefined, mDefinedInScope, mCompiles} + mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles} let scOption = if s.name.id in ctx: scForceOpen else: scOpen case s.kind of skMacro: @@ -177,43 +185,49 @@ proc semGenericStmt(c: PContext, n: PNode, result = semMacroExpr(c, n, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: - n.sons[0] = symChoice(c, n.sons[0], s, scOption) + n.sons[0] = symChoice(c, fn, s, scOption) result = n + mixinContext = true of skTemplate: if macroToExpand(s): styleCheckUse(fn.info, s) - let n = fixImmediateParams(n) result = semTemplateExpr(c, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: - n.sons[0] = symChoice(c, n.sons[0], s, scOption) + n.sons[0] = symChoice(c, fn, s, scOption) result = n # BUGFIX: we must not return here, we need to do first phase of - # symbol lookup ... + # symbol lookup. Also since templates and macros can do scope injections + # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent + # the famous "undeclared identifier: it" bug: + mixinContext = true of skUnknown, skParam: # Leave it as an identifier. discard of skProc, skMethod, skIterators, skConverter: - result.sons[0] = symChoice(c, n.sons[0], s, scOption) + result.sons[0] = symChoice(c, fn, s, scOption) first = 1 of skGenericParam: - result.sons[0] = newSymNodeTypeDesc(s, n.sons[0].info) + result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 of skType: # bad hack for generics: if (s.typ != nil) and (s.typ.kind != tyGenericParam): - result.sons[0] = newSymNodeTypeDesc(s, n.sons[0].info) + result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 else: - result.sons[0] = newSymNode(s, n.sons[0].info) + result.sons[0] = newSymNode(s, fn.info) styleCheckUse(fn.info, s) first = 1 + elif fn.kind == nkDotExpr: + result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) + first = 1 # Consider 'when defined(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # is not exported and yet the generic 'threadProcWrapper' works correctly. - let flags = if isDefinedMagic: flags+{withinMixin} else: flags + let flags = if mixinContext: flags+{withinMixin} else: flags for i in countup(first, sonsLen(result) - 1): result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) of nkIfStmt: @@ -341,7 +355,7 @@ proc semGenericStmt(c: PContext, n: PNode, of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkIteratorDef, nkLambdaKinds: checkSonsLen(n, bodyPos + 1) - if n.kind notin nkLambdaKinds: + if n.sons[namePos].kind != nkEmpty: addTempDecl(c, getIdentNode(n.sons[0]), skProc) openScope(c) n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 8ac3849b4..dd60e0881 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -10,18 +10,15 @@ # This module implements the instantiation of generic procs. # included from sem.nim -proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, - entry: var TInstantiation) = - if n.kind != nkGenericParams: - internalError(n.info, "instantiateGenericParamList; no generic params") - newSeq(entry.concreteTypes, n.len) +iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = + internalAssert n.kind == nkGenericParams for i, a in n.pairs: - if a.kind != nkSym: - internalError(a.info, "instantiateGenericParamList; no symbol") + internalAssert a.kind == nkSym var q = a.sym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: continue - var s = newSym(skType, q.name, getCurrOwner(), q.info) + let symKind = if q.typ.kind == tyStatic: skConst else: skType + var s = newSym(symKind, q.name, getCurrOwner(), q.info) s.flags = s.flags + {sfUsed, sfFromGeneric} var t = PType(idTableGet(pt, q.typ)) if t == nil: @@ -40,8 +37,8 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, t = generateTypeInstance(c, pt, a, t) #t = ReplaceTypeVarsT(cl, t) s.typ = t - addDecl(c, s) - entry.concreteTypes[i] = t + if t.kind == tyStatic: s.ast = t.n + yield s proc sameInstantiation(a, b: TInstantiation): bool = if a.concreteTypes.len == b.concreteTypes.len: @@ -157,8 +154,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 - # + # will need to use openScope, addDecl, etc. addDecl(c, prc) pushInfoContext(info) @@ -195,7 +191,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: - if fn.kind in {skTemplate, skMacro}: return fn + internalAssert fn.kind notin {skMacro, skTemplate} # generates an instantiated proc if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") inc(c.instCounter) @@ -212,18 +208,28 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, result.ast = n pushOwner(result) openScope(c) - internalAssert n.sons[genericParamsPos].kind != nkEmpty + let gp = n.sons[genericParamsPos] + internalAssert gp.kind != nkEmpty n.sons[namePos] = newSymNode(result) pushInfoContext(info) var entry = TInstantiation.new entry.sym = result - instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) + newSeq(entry.concreteTypes, gp.len) + var i = 0 + for s in instantiateGenericParamList(c, gp, pt): + addDecl(c, s) + entry.concreteTypes[i] = s.typ + inc i pushProcCon(c, result) instantiateProcType(c, pt, result, info) n.sons[genericParamsPos] = ast.emptyNode var oldPrc = genericCacheGet(fn, entry[]) if oldPrc == nil: - fn.procInstCache.safeAdd(entry) + # we MUST not add potentially wrong instantiations to the caching mechanism. + # This means recursive instantiations behave differently when in + # a ``compiles`` context but this is the lesser evil. See + # bug #1055 (tevilcompiles). + if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry) c.generics.add(makeInstPair(fn, entry)) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index a72a6ab7d..d6c420955 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -130,4 +130,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mShallowCopy: result = semShallowCopy(c, n, flags) of mNBindSym: result = semBindSym(c, n) of mLocals: result = semLocals(c, n) + of mProcCall: + result = n + result.typ = n[1].typ else: result = n diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index c4546f616..bd3152b54 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -258,14 +258,18 @@ proc min(a, b: PNode): PNode = proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags +template pushSpawnId(c: expr, body: stmt) {.immediate, dirty.} = + inc c.spawns + let oldSpawnId = c.currentSpawnId + c.currentSpawnId = c.spawns + body + c.currentSpawnId = oldSpawnId + proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) = if op.magic == mSpawn: - inc c.spawns - let oldSpawnId = c.currentSpawnId - c.currentSpawnId = c.spawns - gatherArgs(c, n[1]) - analyseSons(c, n) - c.currentSpawnId = oldSpawnId + pushSpawnId(c): + gatherArgs(c, n[1]) + analyseSons(c, n) elif op.magic == mInc or (op.name.s == "+=" and op.fromSystem): if n[1].isLocal: let incr = n[2].skipConv @@ -322,7 +326,14 @@ proc analyse(c: var AnalysisCtx; n: PNode) = let slot = c.getSlot(n[0].sym) slot.blacklisted = true invalidateFacts(c.guards, n[0]) - analyseSons(c, n) + let value = n[1] + if getMagic(value) == mSpawn: + pushSpawnId(c): + gatherArgs(c, value[1]) + analyseSons(c, value[1]) + analyse(c, n[0]) + else: + analyseSons(c, n) addAsgnFact(c.guards, n[0], n[1]) of nkCallKinds: # direct call: @@ -338,13 +349,18 @@ proc analyse(c: var AnalysisCtx; n: PNode) = of nkVarSection, nkLetSection: for it in n: let value = it.lastSon + let isSpawned = getMagic(value) == mSpawn + if isSpawned: + pushSpawnId(c): + gatherArgs(c, value[1]) + analyseSons(c, value[1]) if value.kind != nkEmpty: for j in 0 .. it.len-3: if it[j].isLocal: let slot = c.getSlot(it[j].sym) if slot.lower.isNil: slot.lower = value else: internalError(it.info, "slot already has a lower bound") - analyse(c, value) + if not isSpawned: analyse(c, value) of nkCaseStmt: analyseCase(c, n) of nkIfStmt, nkIfExpr: analyseIf(c, n) of nkWhileStmt: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 0d8c7c479..4300aee20 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -69,7 +69,7 @@ type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags - bottom: int + bottom, inTryStmt: int owner: PSym init: seq[int] # list of initialized variables guards: TModel # nested guards @@ -165,10 +165,17 @@ proc guardDotAccess(a: PEffects; n: PNode) = else: guardGlobal(a, n, g) -proc initVar(a: PEffects, n: PNode) = +proc makeVolatile(a: PEffects; s: PSym) {.inline.} = + template compileToCpp(a): expr = + gCmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags + if a.inTryStmt > 0 and not compileToCpp(a): + incl(s.flags, sfVolatile) + +proc initVar(a: PEffects, n: PNode; volatileCheck: bool) = if n.kind != nkSym: return let s = n.sym if isLocalVar(a, s): + if volatileCheck: makeVolatile(a, s) for x in a.init: if x == s.id: return a.init.add s.id @@ -179,7 +186,9 @@ proc initVarViaNew(a: PEffects, n: PNode) = if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}: # 'x' is not nil, but that doesn't mean its "not nil" children # are initialized: - initVar(a, n) + initVar(a, n, volatileCheck=true) + elif isLocalVar(a, s): + makeVolatile(a, s) proc warnAboutGcUnsafe(n: PNode) = #assert false @@ -304,7 +313,9 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = let oldState = tracked.init.len var inter: TIntersection = @[] - track(tracked, n.sons[0]) + inc tracked.inTryStmt + track(tracked, n.sons[0]) + dec tracked.inTryStmt for i in oldState.. <tracked.init.len: addToIntersection(inter, tracked.init[i]) @@ -359,7 +370,7 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) = # list the computed effects up to here: listEffects(tracked) -proc effectSpec(n: PNode, effectType = wRaises): PNode = +proc effectSpec(n: PNode, effectType: TSpecialWord): PNode = for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it.kind == nkExprColonExpr and whichPragma(it) == effectType: @@ -369,8 +380,7 @@ proc effectSpec(n: PNode, effectType = wRaises): PNode = result.add(it.sons[1]) return -proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int) = - var x = x +proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode = let spec = effectSpec(x, effectType) if isNil(spec): let s = n.sons[namePos].sym @@ -388,18 +398,20 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int) = # set the type so that the following analysis doesn't screw up: effects.sons[i].typ = real[i].typ - var pair = newNode(nkExprColonExpr, n.info, @[ + result = newNode(nkExprColonExpr, n.info, @[ newIdentNode(getIdent(specialWords[effectType]), n.info), effects]) - - if x.kind == nkEmpty: - x = newNodeI(nkPragma, n.info) - n.sons[pragmasPos] = x - x.add(pair) proc documentRaises*(n: PNode) = if n.sons[namePos].kind != nkSym: return - documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects) - documentEffect(n, n.sons[pragmasPos], wTags, tagEffects) + let pragmas = n.sons[pragmasPos] + let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects) + let p2 = documentEffect(n, pragmas, wTags, tagEffects) + + if p1 != nil or p2 != nil: + if pragmas.kind == nkEmpty: + n.sons[pragmasPos] = newNodeI(nkPragma, n.info) + if p1 != nil: n.sons[pragmasPos].add p1 + if p2 != nil: n.sons[pragmasPos].add p2 template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {} @@ -610,7 +622,8 @@ proc track(tracked: PEffects, n: PNode) = useVar(tracked, n) of nkRaiseStmt: n.sons[0].info = n.info - throws(tracked.exc, n.sons[0]) + #throws(tracked.exc, n.sons[0]) + addEffect(tracked, n.sons[0], useLineInfo=false) for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) of nkCallKinds: @@ -660,7 +673,7 @@ proc track(tracked: PEffects, n: PNode) = of nkPragma: trackPragmaStmt(tracked, n) of nkAsgn, nkFastAsgn: track(tracked, n.sons[1]) - initVar(tracked, n.sons[0]) + initVar(tracked, n.sons[0], volatileCheck=true) invalidateFacts(tracked.guards, n.sons[0]) track(tracked, n.sons[0]) addAsgnFact(tracked.guards, n.sons[0], n.sons[1]) @@ -672,7 +685,7 @@ proc track(tracked: PEffects, n: PNode) = if child.kind == nkIdentDefs and last.kind != nkEmpty: track(tracked, last) for i in 0 .. child.len-3: - initVar(tracked, child.sons[i]) + initVar(tracked, child.sons[i], volatileCheck=false) addAsgnFact(tracked.guards, child.sons[i], last) notNilCheck(tracked, last, child.sons[i].typ) # since 'var (a, b): T = ()' is not even allowed, there is always type diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c6906d98e..1396ef374 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -263,7 +263,8 @@ proc semTry(c: PContext, n: PNode): PNode = n.sons[0] = semExprBranchScope(c, n.sons[0]) typ = commonType(typ, n.sons[0].typ) var check = initIntSet() - for i in countup(1, sonsLen(n) - 1): + var last = sonsLen(n) - 1 + for i in countup(1, last): var a = n.sons[i] checkMinSonsLen(a, 1) var length = sonsLen(a) @@ -282,11 +283,12 @@ proc semTry(c: PContext, n: PNode): PNode = a.sons[j].typ = typ if containsOrIncl(check, typ.id): localError(a.sons[j].info, errExceptionAlreadyHandled) - elif a.kind != nkFinally: + elif a.kind != nkFinally: illFormedAst(n) # last child of an nkExcept/nkFinally branch is a statement: a.sons[length-1] = semExprBranchScope(c, a.sons[length-1]) - typ = commonType(typ, a.sons[length-1].typ) + if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ) + else: dec last dec c.p.inTryStmt if isEmptyType(typ) or typ.kind == tyNil: discardCheck(c, n.sons[0]) @@ -294,13 +296,14 @@ proc semTry(c: PContext, n: PNode): PNode = if typ == enforceVoidContext: result.typ = enforceVoidContext else: + if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon) n.sons[0] = fitNode(c, typ, n.sons[0]) - for i in 1..n.len-1: + for i in 1..last: var it = n.sons[i] let j = it.len-1 it.sons[j] = fitNode(c, typ, it.sons[j]) result.typ = typ - + proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result = fitNode(c, typ, n) if result.kind in {nkHiddenStdConv, nkHiddenSubConv}: @@ -364,6 +367,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # BUGFIX: ``fitNode`` is needed here! # check type compability between def.typ and typ def = fitNode(c, typ, def) + #changeType(def.skipConv, typ, check=true) else: typ = skipIntLit(def.typ) if typ.kind in {tySequence, tyArray, tySet} and @@ -465,153 +469,7 @@ proc semConst(c: PContext, n: PNode): PNode = addSon(b, copyTree(def)) addSon(result, b) -type - TFieldInstCtx = object # either 'tup[i]' or 'field' is valid - tupleType: PType # if != nil we're traversing a tuple - tupleIndex: int - field: PSym - replaceByFieldName: bool - -proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = - case n.kind - of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n - of nkIdent: - result = n - var L = sonsLen(forLoop) - if c.replaceByFieldName: - if n.ident.id == forLoop[0].ident.id: - let fieldName = if c.tupleType.isNil: c.field.name.s - elif c.tupleType.n.isNil: "Field" & $c.tupleIndex - else: c.tupleType.n.sons[c.tupleIndex].sym.name.s - result = newStrNode(nkStrLit, fieldName) - return - # other fields: - for i in ord(c.replaceByFieldName)..L-3: - if n.ident.id == forLoop[i].ident.id: - var call = forLoop.sons[L-2] - var tupl = call.sons[i+1-ord(c.replaceByFieldName)] - if c.field.isNil: - result = newNodeI(nkBracketExpr, n.info) - result.add(tupl) - result.add(newIntNode(nkIntLit, c.tupleIndex)) - else: - result = newNodeI(nkDotExpr, n.info) - result.add(tupl) - result.add(newSymNode(c.field, n.info)) - break - else: - if n.kind == nkContinueStmt: - localError(n.info, errGenerated, - "'continue' not supported in a 'fields' loop") - result = copyNode(n) - newSons(result, sonsLen(n)) - for i in countup(0, sonsLen(n)-1): - result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop) - -type - TFieldsCtx = object - c: PContext - m: TMagic - -proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = - case typ.kind - of nkSym: - var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid - fc.field = typ.sym - fc.replaceByFieldName = c.m == mFieldPairs - openScope(c.c) - inc c.c.inUnrolledContext - let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) - father.add(semStmt(c.c, body)) - dec c.c.inUnrolledContext - closeScope(c.c) - of nkNilLit: discard - of nkRecCase: - let L = forLoop.len - let call = forLoop.sons[L-2] - if call.len > 2: - localError(forLoop.info, errGenerated, - "parallel 'fields' iterator does not work for 'case' objects") - return - # iterate over the selector: - semForObjectFields(c, typ[0], forLoop, father) - # we need to generate a case statement: - var caseStmt = newNodeI(nkCaseStmt, forLoop.info) - # generate selector: - var access = newNodeI(nkDotExpr, forLoop.info, 2) - access.sons[0] = call.sons[1] - access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info) - caseStmt.add(semExprWithType(c.c, access)) - # copy the branches over, but replace the fields with the for loop body: - for i in 1 .. <typ.len: - var branch = copyTree(typ[i]) - let L = branch.len - branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info) - semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1]) - caseStmt.add(branch) - father.add(caseStmt) - of nkRecList: - for t in items(typ): semForObjectFields(c, t, forLoop, father) - else: - illFormedAst(typ) - -proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = - # so that 'break' etc. work as expected, we produce - # a 'while true: stmt; break' loop ... - result = newNodeI(nkWhileStmt, n.info, 2) - var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true") - if trueSymbol == nil: - localError(n.info, errSystemNeeds, "true") - trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info) - trueSymbol.typ = getSysType(tyBool) - - result.sons[0] = newSymNode(trueSymbol, n.info) - var stmts = newNodeI(nkStmtList, n.info) - result.sons[1] = stmts - - var length = sonsLen(n) - var call = n.sons[length-2] - if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs): - localError(n.info, errWrongNumberOfVariables) - return result - - var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc}) - if tupleTypeA.kind notin {tyTuple, tyObject}: - localError(n.info, errGenerated, "no object or tuple type") - return result - for i in 1..call.len-1: - var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc}) - if not sameType(tupleTypeA, tupleTypeB): - typeMismatch(call.sons[i], tupleTypeA, tupleTypeB) - - inc(c.p.nestedLoopCounter) - if tupleTypeA.kind == tyTuple: - var loopBody = n.sons[length-1] - for i in 0..sonsLen(tupleTypeA)-1: - openScope(c) - var fc: TFieldInstCtx - fc.tupleType = tupleTypeA - fc.tupleIndex = i - fc.replaceByFieldName = m == mFieldPairs - var body = instFieldLoopBody(fc, loopBody, n) - inc c.inUnrolledContext - stmts.add(semStmt(c, body)) - dec c.inUnrolledContext - closeScope(c) - else: - var fc: TFieldsCtx - fc.m = m - fc.c = c - semForObjectFields(fc, tupleTypeA.n, n, stmts) - dec(c.p.nestedLoopCounter) - # for TR macros this 'while true: ...; break' loop is pretty bad, so - # we avoid it now if we can: - if hasSonWith(stmts, nkBreakStmt): - var b = newNodeI(nkBreakStmt, n.info) - b.add(ast.emptyNode) - stmts.add(b) - else: - result = stmts +include semfields proc addForVarDecl(c: PContext, v: PSym) = if warnShadowIdent in gNotes: @@ -795,7 +653,8 @@ proc checkForMetaFields(n: PNode) = template checkMeta(t) = if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: localError(n.info, errTIsNotAConcreteType, t.typeToString) - + + if n.isNil: return case n.kind of nkRecList, nkRecCase: for s in n: checkForMetaFields(s) @@ -918,7 +777,7 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode = if it.kind == nkExprColonExpr: # pass pragma argument to the macro too: x.add(it.sons[1]) - x.add(newProcNode(nkDo, prc.info, prc)) + x.add(prc) # recursion assures that this works for multiple macro annotations too: return semStmt(c, x) @@ -946,6 +805,9 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = gp = newNodeI(nkGenericParams, n.info) if n.sons[paramsPos].kind != nkEmpty: + #if n.kind == nkDo and not experimentalMode(c): + # localError(n.sons[paramsPos].info, + # "use the {.experimental.} pragma to enable 'do' with parameters") semParamList(c, n.sons[paramsPos], gp, s) # paramsTypeCheck(c, s.typ) if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty: @@ -1026,7 +888,10 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) = case s.name.s.normalize - of "destroy": doDestructorStuff(c, s, n) + of "destroy": + doDestructorStuff(c, s, n) + if not experimentalMode(c): + localError n.info, "use the {.experimental.} pragma to enable destructors" of "deepcopy": if s.typ.len == 2 and s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and @@ -1362,8 +1227,9 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # nkNilLit, nkEmpty}: # dec last for i in countup(0, length - 1): - case n.sons[i].kind - of nkFinally, nkExceptBranch: + let k = n.sons[i].kind + case k + of nkFinally, nkExceptBranch, nkDefer: # stand-alone finally and except blocks are # transformed into regular try blocks: # @@ -1372,21 +1238,38 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # ... | ... # | finally: # | fclose(f) + var deferPart: PNode + if k == nkDefer: + deferPart = newNodeI(nkFinally, n.sons[i].info) + deferPart.add n.sons[i].sons[0] + elif k == nkFinally: + message(n.info, warnDeprecated, + "use 'defer'; standalone 'finally'") + deferPart = n.sons[i] + else: + message(n.info, warnDeprecated, + "use an explicit 'try'; standalone 'except'") + deferPart = n.sons[i] var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) var body = newNodeI(nkStmtList, n.sons[i].info) if i < n.sonsLen - 1: body.sons = n.sons[(i+1)..(-1)] tryStmt.addSon(body) - tryStmt.addSon(n.sons[i]) + tryStmt.addSon(deferPart) n.sons[i] = semTry(c, tryStmt) n.sons.setLen(i+1) + n.typ = n.sons[i].typ return else: n.sons[i] = semExpr(c, n.sons[i]) - if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool: - let verdict = semConstExpr(c, n[i]) - if verdict.intVal == 0: - localError(result.info, "type class predicate failed") + if c.inTypeClass > 0 and n[i].typ != nil: + case n[i].typ.kind + of tyBool: + let verdict = semConstExpr(c, n[i]) + if verdict.intVal == 0: + localError(result.info, "type class predicate failed") + of tyUnknown: continue + else: discard if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): voidContext = true n.typ = enforceVoidContext @@ -1424,7 +1307,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # a statement list (s; e) has the type 'e': if result.kind == nkStmtList and result.len > 0: var lastStmt = lastSon(result) - if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt): + if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt): result.typ = lastStmt.typ #localError(lastStmt.info, errGenerated, # "Last expression must be explicitly returned if it " & diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 0a647a65d..38b0536db 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -63,7 +63,8 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately - let kind = if r == scClosed: nkClosedSymChoice else: nkOpenSymChoice + let kind = if r == scClosed or n.kind == nkDotExpr: nkClosedSymChoice + else: nkOpenSymChoice result = newNodeIT(kind, n.info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b3ff6c82a..deb4b1288 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -170,9 +170,10 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = range[1] = semExprWithType(c, n[2], {efDetermineType}) var rangeT: array[2, PType] - for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit + for i in 0..1: + rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit - if not sameType(rangeT[0], rangeT[1]): + if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): localError(n.info, errPureTypeMismatch) elif not rangeT[0].isOrdinalType: localError(n.info, errOrdinalTypeExpected) @@ -213,44 +214,47 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = localError(n.info, errXExpectsOneTypeParam, "range") result = newOrPrevType(tyError, prev, c) +proc semArrayIndex(c: PContext, n: PNode): PType = + if isRange(n): result = semRangeAux(c, n, nil) + else: + let e = semExprWithType(c, n, {efDetermineType}) + if e.typ.kind == tyFromExpr: + result = makeRangeWithStaticExpr(c, e.typ.n) + elif e.kind in {nkIntLit..nkUInt64Lit}: + result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) + elif e.kind == nkSym and e.typ.kind == tyStatic: + if e.sym.ast != nil: + return semArrayIndex(c, e.sym.ast) + if not isOrdinalType(e.typ.lastSon): + localError(n[1].info, errOrdinalTypeExpected) + result = makeRangeWithStaticExpr(c, e) + if c.inGenericContext >0: result.flags.incl tfUnresolved + elif e.kind in nkCallKinds and hasGenericArguments(e): + if not isOrdinalType(e.typ): + localError(n[1].info, errOrdinalTypeExpected) + # This is an int returning call, depending on an + # yet unknown generic param (see tgenericshardcases). + # We are going to construct a range type that will be + # properly filled-out in semtypinst (see how tyStaticExpr + # is handled there). + result = makeRangeWithStaticExpr(c, e) + elif e.kind == nkIdent: + result = e.typ.skipTypes({tyTypeDesc}) + else: + let x = semConstExpr(c, e) + if x.kind in {nkIntLit..nkUInt64Lit}: + result = makeRangeType(c, 0, x.intVal-1, n.info, + x.typ.skipTypes({tyTypeDesc})) + else: + result = x.typ.skipTypes({tyTypeDesc}) + #localError(n[1].info, errConstExprExpected) + proc semArray(c: PContext, n: PNode, prev: PType): PType = - var indx, base: PType + var base: PType result = newOrPrevType(tyArray, prev, c) - if sonsLen(n) == 3: + if sonsLen(n) == 3: # 3 = length(array indx base) - if isRange(n[1]): indx = semRangeAux(c, n[1], nil) - else: - let e = semExprWithType(c, n.sons[1], {efDetermineType}) - if e.typ.kind == tyFromExpr: - indx = makeRangeWithStaticExpr(c, e.typ.n) - elif e.kind in {nkIntLit..nkUInt64Lit}: - indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) - elif e.kind == nkSym and e.typ.kind == tyStatic: - if e.sym.ast != nil: return semArray(c, e.sym.ast, nil) - internalAssert c.inGenericContext > 0 - if not isOrdinalType(e.typ.lastSon): - localError(n[1].info, errOrdinalTypeExpected) - indx = makeRangeWithStaticExpr(c, e) - indx.flags.incl tfUnresolved - elif e.kind in nkCallKinds and hasGenericArguments(e): - if not isOrdinalType(e.typ): - localError(n[1].info, errOrdinalTypeExpected) - # This is an int returning call, depending on an - # yet unknown generic param (see tgenericshardcases). - # We are going to construct a range type that will be - # properly filled-out in semtypinst (see how tyStaticExpr - # is handled there). - indx = makeRangeWithStaticExpr(c, e) - elif e.kind == nkIdent: - indx = e.typ.skipTypes({tyTypeDesc}) - else: - let x = semConstExpr(c, e) - if x.kind in {nkIntLit..nkUInt64Lit}: - indx = makeRangeType(c, 0, x.intVal-1, n.info, - x.typ.skipTypes({tyTypeDesc})) - else: - indx = x.typ.skipTypes({tyTypeDesc}) - #localError(n[1].info, errConstExprExpected) + var indx = semArrayIndex(c, n[1]) addSonSkipIntLit(result, indx) if indx.kind == tyGenericInst: indx = lastSon(indx) if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}: @@ -385,13 +389,13 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, else: result = newSymG(kind, n, c) -proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym = - if n.kind == nkPragmaExpr: +proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, + allowed: TSymFlags): PSym = + if n.kind == nkPragmaExpr: checkSonsLen(n, 2) result = semIdentVis(c, kind, n.sons[0], allowed) case kind - of skType: + of skType: # process pragmas later, because result.typ has not been set yet discard of skField: pragma(c, result, n.sons[1], fieldPragmas) @@ -402,7 +406,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, else: result = semIdentVis(c, kind, n, allowed) if gCmd == cmdPretty: styleCheckDef(n.info, result) - + proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = let ex = t[branchIndex][currentEx].skipConv for i in countup(1, branchIndex): @@ -777,10 +781,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyGenericBody: result = newTypeS(tyGenericInvokation, c) result.rawAddSon(paramType) + for i in 0 .. paramType.sonsLen - 2: - result.rawAddSon newTypeS(tyAnything, c) - # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) - + let dummyType = if paramType.sons[i].kind == tyStatic: tyUnknown + else: tyAnything + result.rawAddSon newTypeS(dummyType, c) + if paramType.lastSon.kind == tyUserTypeClass: result.kind = tyUserTypeClassInst result.rawAddSon paramType.lastSon @@ -853,7 +859,9 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = result = semTypeNode(c, n, nil) proc semProcTypeNode(c: PContext, n, genericParams: PNode, - prev: PType, kind: TSymKind): PType = + prev: PType, kind: TSymKind; isType=false): PType = + # for historical reasons (code grows) this is invoked for parameter + # lists too and then 'isType' is false. var res: PNode cl: IntSet @@ -902,7 +910,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # proc sort[T](cmp: proc(a, b: T): int = cmp) if not containsGenericType(typ): def = fitNode(c, typ, def) - if not (hasType or hasDefault): + if not hasType and not hasDefault: + if isType: localError(a.info, "':' expected") let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything typ = newTypeS(tdef, c) @@ -928,23 +937,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if n.sons[0].kind != nkEmpty: r = semTypeNode(c, n.sons[0], nil) elif kind == skIterator: + # XXX This is special magic we should likely get rid of r = newTypeS(tyAnything, c) 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 r.sym == nil or sfAnon notin r.sym.flags: - let lifted = liftParamType(c, kind, genericParams, r, "result", - n.sons[0].info) - if lifted != nil: r = lifted - r.flags.incl tfRetType - r = skipIntLit(r) - if kind == skIterator: - # see tchainediterators - # in cases like iterator foo(it: iterator): type(it) - # we don't need to change the return type to iter[T] - if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r]) + # 'auto' as a return type does not imply a generic: + if r.kind != tyExpr: + if r.sym == nil or sfAnon notin r.sym.flags: + let lifted = liftParamType(c, kind, genericParams, r, "result", + n.sons[0].info) + if lifted != nil: r = lifted + r.flags.incl tfRetType + r = skipIntLit(r) + if kind == skIterator: + # see tchainediterators + # in cases like iterator foo(it: iterator): type(it) + # we don't need to change the return type to iter[T] + if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r]) result.sons[0] = r res.typ = r @@ -1068,7 +1080,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode, prev: PType, kind: TSymKind): PType = checkSonsLen(n, 2) openScope(c) - result = semProcTypeNode(c, n.sons[0], nil, prev, kind) + result = semProcTypeNode(c, n.sons[0], nil, prev, kind, isType=true) # dummy symbol for `pragma`: var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c) s.typ = result diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 9c15be635..e069064c2 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -291,8 +291,9 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = var newbody = replaceTypeVarsT(cl, lastSon(body)) newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) - result.flags = result.flags + newbody.flags - newbody.callConv = body.callConv + result.flags = result.flags + newbody.flags - tfInstClearedFlags + # This is actually wrong: tgeneric_closure fails with this line: + #newbody.callConv = body.callConv # This type may be a generic alias and we want to resolve it here. # One step is enough, because the recursive nature of # handleGenericInvokation will handle the alias-to-alias-to-alias case @@ -306,6 +307,8 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info) 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: t.sons[0] = nil diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4a3773ed8..721f7e318 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -23,7 +23,7 @@ type csEmpty, csMatch, csNoMatch CandidateErrors* = seq[PSym] - TCandidate* {.final.} = object + TCandidate* = object c*: PContext exactMatches*: int # also misused to prefer iters over procs genericMatches: int # also misused to prefer constraints @@ -39,7 +39,9 @@ type bindings*: TIdTable # maps types to types baseTypeMatch: bool # needed for conversions from T to openarray[T] # for example - proxyMatch*: bool # to prevent instantiations + fauxMatch*: TTypeKind # the match was successful only due to the use + # of error or wildcard (unknown) types. + # this is used to prevent instantiations. genericConverter*: bool # true if a generic converter needs to # be instantiated coerceDistincts*: bool # this is an explicit coercion that can strip away @@ -66,6 +68,8 @@ const proc markUsed*(info: TLineInfo, s: PSym) +template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone + proc initCandidateAux(ctx: PContext, c: var TCandidate, callee: PType) {.inline.} = c.c = ctx @@ -109,9 +113,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): var formalTypeParam = typeParams.sons[i-1].typ var bound = binding[i].typ - if bound != nil and formalTypeParam.kind != tyTypeDesc: + internalAssert bound != nil + if formalTypeParam.kind == tyTypeDesc: + if bound.kind != tyTypeDesc: + bound = makeTypeDesc(ctx, bound) + else: bound = bound.skipTypes({tyTypeDesc}) - assert bound != nil put(c.bindings, formalTypeParam, bound) proc newCandidate*(ctx: PContext, callee: PSym, @@ -242,6 +249,9 @@ proc concreteType(c: TCandidate, t: PType): PType = addSonSkipIntLit(result, t.sons[1]) # XXX: semantic checking for the type? of tyNil: result = nil # what should it be? + of tySequence, tySet: + if t.sons[0].kind == tyEmpty: result = nil + else: result = t of tyGenericParam, tyAnything: result = t while true: @@ -346,7 +356,7 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = var y = a.n.sons[i].sym if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype: return isNone - if x.name.id != y.name.id: return isNone + if x.name.id != y.name.id and f.kind != tyTuple: return isNone proc allowsNil(f: PType): TTypeRelation {.inline.} = result = if tfNotNil notin f.flags: isSubtype else: isNone @@ -459,9 +469,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, var typeParamName = ff.base.sons[i-1].sym.name typ = ff.sons[i] - param = newSym(skType, typeParamName, body.sym, body.sym.info) - - param.typ = makeTypeDesc(c, typ) + param: PSym + + template paramSym(kind): expr = + newSym(kind, typeParamName, body.sym, body.sym.info) + + case typ.kind + of tyStatic: + param = paramSym skConst + param.typ = typ.base + param.ast = typ.n + of tyUnknown: + param = paramSym skVar + param.typ = typ + else: + param = paramSym skType + param.typ = makeTypeDesc(c, typ) + addDecl(c, param) for param in body.n[0]: @@ -514,6 +538,16 @@ proc maybeSkipDistinct(t: PType, callee: PSym): PType = else: result = t +proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode = + # Consider this example: + # type Value[N: static[int]] = object + # proc foo[N](a: Value[N], r: range[0..(N-1)]) + # Here, N-1 will be initially nkStaticExpr that can be evaluated only after + # N is bound to a concrete value during the matching of the first param. + # This proc is used to evaluate such static expressions. + let instantiated = replaceTypesInBody(c.c, c.bindings, n) + result = c.c.semExpr(c.c, instantiated) + proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # typeRel can be used to establish various relationships between types: # @@ -613,10 +647,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype of tyRange: if a.kind == f.kind: + if f.base.kind == tyNone: return isGeneric result = typeRel(c, base(f), base(a)) # bugfix: accept integer conversions here #if result < isGeneric: result = isNone if result notin {isNone, isGeneric}: + # resolve any late-bound static expressions + # that may appear in the range: + for i in 0..1: + if f.n[i].kind == nkStaticExpr: + f.n.sons[i] = tryResolvingStaticExpr(c, f.n[i]) result = typeRangeRel(f, a) else: if skipTypes(f, {tyRange}).kind == a.kind: @@ -654,22 +694,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: fRange = prev result = typeRel(c, f.sons[1], a.sons[1]) - if result < isGeneric: - result = isNone - elif tfUnresolved in fRange.flags and - rangeHasStaticIf(fRange): - # This is a range from an array instantiated with a generic - # static param. We must extract the static param here and bind - # it to the size of the currently supplied array. - var - rangeStaticT = fRange.getStaticTypeFromRange - replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType]) - inputUpperBound = a.sons[0].n[1].intVal - # 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) - result = isGeneric + if result < isGeneric: return isNone + if rangeHasStaticIf(fRange): + if tfUnresolved in fRange.flags: + # This is a range from an array instantiated with a generic + # static param. We must extract the static param here and bind + # it to the size of the currently supplied array. + var + rangeStaticT = fRange.getStaticTypeFromRange + replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType]) + inputUpperBound = a.sons[0].n[1].intVal + # 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) + return isGeneric + + let len = tryResolvingStaticExpr(c, fRange.n[1]) + if len.kind == nkIntLit and len.intVal+1 == lengthOrd(a): + return # if we get this far, the result is already good + else: + return isNone elif lengthOrd(fRange) != lengthOrd(a): result = isNone else: discard @@ -927,12 +972,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: internalAssert a.sons != nil and a.sons.len > 0 c.typedescMatched = true - result = typeRel(c, f.base, a.base) + result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc})) else: result = isNone else: if f.sonsLen > 0 and f.sons[0].kind != tyNone: result = typeRel(c, f.lastSon, a) + if doBind and result notin {isNone, isGeneric}: + let concrete = concreteType(c, a) + if concrete == nil: return isNone + put(c.bindings, f, concrete) else: result = isGeneric @@ -955,12 +1004,19 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, x, a) # check if it fits of tyStatic: - if aOrig.kind == tyStatic: - result = typeRel(c, f.lastSon, a) - if result != isNone: put(c.bindings, f, aOrig) + let prev = PType(idTableGet(c.bindings, f)) + if prev == nil: + if aOrig.kind == tyStatic: + result = typeRel(c, f.lastSon, a) + if result != isNone and f.n != nil: + if not exprStructuralEquivalent(f.n, aOrig.n): + result = isNone + if result != isNone: put(c.bindings, f, aOrig) + else: + result = isNone else: - result = isNone - + result = typeRel(c, prev, aOrig) + of tyTypeDesc: var prev = PType(idTableGet(c.bindings, f)) if prev == nil: @@ -999,15 +1055,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyFromExpr: # fix the expression, so it contains the already instantiated types - let instantiated = replaceTypesInBody(c.c, c.bindings, f.n) - let reevaluted = c.c.semExpr(c.c, instantiated) - case reevaluted.typ.kind + if f.n == nil: return isGeneric + let reevaluated = tryResolvingStaticExpr(c, f.n) + case reevaluated.typ.kind of tyTypeDesc: - result = typeRel(c, a, reevaluted.typ.base) + result = typeRel(c, a, reevaluated.typ.base) of tyStatic: - result = typeRel(c, a, reevaluted.typ.base) - if result != isNone and reevaluted.typ.n != nil: - if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n): + result = typeRel(c, a, reevaluated.typ.base) + if result != isNone and reevaluated.typ.n != nil: + if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): result = isNone else: localError(f.n.info, errTypeExpected) @@ -1021,10 +1077,10 @@ proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = initCandidate(c, m, f) result = typeRel(m, f, a) -proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, - f: PType): PType = +proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, + f: PType): PType = result = PType(idTableGet(m.bindings, f)) - if result == nil: + if result == nil: result = generateTypeInstance(c, m.bindings, arg, f) if result == nil: internalError(arg.info, "getInstantiatedType") @@ -1034,7 +1090,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, c: PContext): PNode = result = newNodeI(kind, arg.info) if containsGenericType(f): - if not m.proxyMatch: + if not m.hasFauxMatch: result.typ = getInstantiatedType(c, arg, m, f) else: result.typ = errorType(c) @@ -1076,6 +1132,11 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, arg: PNode): PNode = # arg.typ can be nil in 'suggest': if isNil(arg.typ): return nil + + # sem'checking for 'echo' needs to be re-entrant: + # XXX we will revisit this issue after 0.10.2 is released + if f == arg.typ and arg.kind == nkHiddenStdConv: return arg + var call = newNodeI(nkCall, arg.info) call.add(f.n.copyTree) call.add(arg.copyTree) @@ -1101,7 +1162,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg = argSemantized argType = argType c = m.c - + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to @@ -1115,7 +1176,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, return argSemantized if argType.kind == tyStatic: - if m.callee.kind == tyGenericBody: + if m.callee.kind == tyGenericBody and tfGenericTypeParam notin argType.flags: result = newNodeI(nkType, argOrig.info) result.typ = makeTypeFromExpr(c, arg) return @@ -1146,8 +1207,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, of isEqual: inc(m.exactMatches) of isNone: discard - if f.kind == tyStmt and argOrig.kind == nkDo: - return argOrig[bodyPos] + if f.kind == tyStmt: + return arg elif f.kind == tyTypeDesc: return arg elif f.kind == tyStatic: @@ -1208,9 +1269,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isNone: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``: - if a.kind == tyProxy: + if a.kind in {tyProxy, tyUnknown}: inc(m.genericMatches) - m.proxyMatch = true + m.fauxMatch = a.kind return copyTree(arg) result = userConvMatch(c, m, f, a, arg) # check for a base type match, which supports varargs[T] without [] diff --git a/compiler/suggest.nim b/compiler/suggest.nim index c700db323..f7b00c8f8 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -74,9 +74,6 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) = suggestWriteln(symToStr(s, isLocal=true, sectionSuggest)) inc outputs -when not defined(nimhygiene): - {.pragma: inject.} - template wholeSymTab(cond, section: expr) {.immediate.} = var isLocal = true for scope in walkScopes(c.currentScope): diff --git a/compiler/transf.nim b/compiler/transf.nim index 6196512ba..3409acb74 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -582,7 +582,7 @@ proc getMergeOp(n: PNode): PSym = else: discard proc flattenTreeAux(d, a: PNode, op: PSym) = - var op2 = getMergeOp(a) + let op2 = getMergeOp(a) if op2 != nil and (op2.id == op.id or op.magic != mNone and op2.magic == op.magic): for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op) @@ -590,7 +590,7 @@ proc flattenTreeAux(d, a: PNode, op: PSym) = addSon(d, copyTree(a)) proc flattenTree(root: PNode): PNode = - var op = getMergeOp(root) + let op = getMergeOp(root) if op != nil: result = copyNode(root) addSon(result, copyTree(root.sons[0])) @@ -600,8 +600,9 @@ proc flattenTree(root: PNode): PNode = proc transformCall(c: PTransf, n: PNode): PTransNode = var n = flattenTree(n) - var op = getMergeOp(n) - if (op != nil) and (op.magic != mNone) and (sonsLen(n) >= 3): + let op = getMergeOp(n) + let magic = getMagic(n) + if op != nil and op.magic != mNone and n.len >= 3: result = newTransNode(nkCall, n, 0) add(result, transform(c, n.sons[0])) var j = 1 @@ -616,9 +617,12 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = inc(j) add(result, a.PTransNode) if len(result) == 2: result = result[1] - elif getMagic(n) == mNBindSym: + elif magic == mNBindSym: # for bindSym(myconst) we MUST NOT perform constant folding: result = n.PTransNode + elif magic == mProcCall: + # but do not change to its dispatcher: + result = transformSons(c, n[1]) else: let s = transformSons(c, n).PNode # bugfix: check after 'transformSons' if it's still a method call: diff --git a/compiler/trees.nim b/compiler/trees.nim index b1edd21f3..86a1139a0 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -117,7 +117,9 @@ proc isDeepConstExpr*(n: PNode): bool = of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure: for i in 0 .. <n.len: if not isDeepConstExpr(n.sons[i]): return false - result = true + # 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 else: discard proc flattenTreeAux(d, a: PNode, op: TMagic) = diff --git a/compiler/types.nim b/compiler/types.nim index 87f2e1a59..4a77773e6 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -17,7 +17,7 @@ proc lastOrd*(t: PType): BiggestInt proc lengthOrd*(t: PType): BiggestInt type TPreferedDesc* = enum - preferName, preferDesc, preferExported, preferModuleInfo + preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string proc base*(t: PType): PType @@ -411,11 +411,13 @@ const "UserTypeClassInst", "CompositeTypeClass", "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"] +const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg} + proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = var t = typ result = "" if t == nil: return - if prefer in {preferName, preferModuleInfo} and t.sym != nil and + if prefer in preferToResolveSymbols and t.sym != nil and sfAnon notin t.sym.flags: if t.kind == tyInt and isIntLit(t): return t.sym.name.s & " literal(" & $t.n.intVal & ")" @@ -428,20 +430,26 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if not isIntLit(t) or prefer == preferExported: result = typeToStr[t.kind] else: - result = "int literal(" & $t.n.intVal & ")" + if prefer == preferGenericArg: + result = $t.n.intVal + else: + result = "int literal(" & $t.n.intVal & ")" of tyGenericBody, tyGenericInst, tyGenericInvokation: result = typeToString(t.sons[0]) & '[' for i in countup(1, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)): if i > 1: add(result, ", ") - add(result, typeToString(t.sons[i])) + add(result, typeToString(t.sons[i], preferGenericArg)) add(result, ']') of tyTypeDesc: if t.base.kind == tyNone: result = "typedesc" else: result = "typedesc[" & typeToString(t.base) & "]" of tyStatic: internalAssert t.len > 0 - result = "static[" & typeToString(t.sons[0]) & "]" - if t.n != nil: result.add "(" & renderTree(t.n) & ")" + if prefer == preferGenericArg and t.n != nil: + result = t.n.renderTree + else: + result = "static[" & typeToString(t.sons[0]) & "]" + if t.n != nil: result.add "(" & renderTree(t.n) & ")" of tyUserTypeClass: internalAssert t.sym != nil and t.sym.owner != nil return t.sym.owner.name.s @@ -762,7 +770,7 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = # two tuples are equivalent iff the names, types and positions are the same; # however, both types may not have any field names (t.n may be nil) which # complicates the matter a bit. - if sonsLen(a) == sonsLen(b): + if sonsLen(a) == sonsLen(b): result = true for i in countup(0, sonsLen(a) - 1): var x = a.sons[i] @@ -773,17 +781,6 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = result = sameTypeAux(x, y, c) if not result: return - if a.n != nil and b.n != nil and IgnoreTupleFields notin c.flags: - for i in countup(0, sonsLen(a.n) - 1): - # check field names: - if a.n.sons[i].kind == nkSym and b.n.sons[i].kind == nkSym: - var x = a.n.sons[i].sym - var y = b.n.sons[i].sym - result = x.name.id == y.name.id - if not result: break - else: internalError(a.n.info, "sameTuple") - else: - result = false template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = if tfFromGeneric notin a.flags + b.flags: diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index f3121a0b4..432a26666 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -1,3 +1,12 @@ +# +# +# The Nim Compiler +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + import renderer, strutils, ast, msgs, types, astalgo const defaultParamSeparator* = "," @@ -26,7 +35,7 @@ proc renderPlainSymbolName*(n: PNode): string = result = renderPlainSymbolName(n[<n.len]) else: internalError(n.info, "renderPlainSymbolName() with " & $n.kind) - assert (not result.isNil) + assert(not result.isNil) proc renderType(n: PNode): string = ## Returns a string with the node type or the empty string. diff --git a/compiler/vm.nim b/compiler/vm.nim index a15807b24..4072ed765 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This file implements the new evaluation engine for Nimrod code. +## 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 @@ -328,7 +328,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = myreset(dest); dest.kind = rkInt case skipTypes(srctyp, abstractRange).kind of tyFloat..tyFloat64: - dest.intVal = system.toInt(src.floatVal) + dest.intVal = int(src.floatVal) else: dest.intVal = src.intVal if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp): @@ -338,7 +338,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = myreset(dest); dest.kind = rkInt case skipTypes(srctyp, abstractRange).kind of tyFloat..tyFloat64: - dest.intVal = system.toInt(src.floatVal) + dest.intVal = int(src.floatVal) else: dest.intVal = src.intVal and ((1 shl (desttyp.size*8))-1) of tyFloat..tyFloat64: @@ -454,8 +454,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdStrIdx: decodeBC(rkInt) let idx = regs[rc].intVal.int - if idx <=% regs[rb].node.strVal.len: - regs[ra].intVal = regs[rb].node.strVal[idx].ord + let s = regs[rb].node.strVal + if s.isNil: + stackTrace(c, tos, pc, errNilAccess) + elif idx <=% s.len: + regs[ra].intVal = s[idx].ord else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArr: @@ -1058,7 +1061,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1) of opcIsNil: decodeB(rkInt) - regs[ra].intVal = ord(regs[rb].node.kind == nkNilLit) + let node = regs[rb].node + regs[ra].intVal = ord(node.kind == nkNilLit or + (node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil)) of opcNBindSym: decodeBx(rkNode) regs[ra].node = copyTree(c.constants.sons[rbx]) @@ -1151,7 +1156,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath, c.debug[pc].line.int, proc (info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil: error = formatMsg(info, msg, arg)) + if error.isNil and msg <= msgs.errMax: + error = formatMsg(info, msg, arg)) if not error.isNil: c.errorFlag = error elif sonsLen(ast) != 1: @@ -1164,7 +1170,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath, c.debug[pc].line.int, proc (info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil: error = formatMsg(info, msg, arg)) + if error.isNil and msg <= msgs.errMax: + error = formatMsg(info, msg, arg)) if not error.isNil: c.errorFlag = error else: @@ -1172,6 +1179,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcQueryErrorFlag: createStr regs[ra] regs[ra].node.strVal = c.errorFlag + c.errorFlag.setLen 0 of opcCallSite: ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite @@ -1409,12 +1417,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = proc setupCompileTimeVar*(module: PSym, n: PNode) = discard evalConstExprAux(module, nil, n, emStaticStmt) -proc setupMacroParam(x: PNode): PNode = - result = x - if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] - result = canonValue(result) - result.flags.incl nfIsRef - result.typ = x.typ +proc setupMacroParam(x: PNode, typ: PType): TFullReg = + case typ.kind + of tyStatic: + putIntoReg(result, x) + of tyTypeDesc: + putIntoReg(result, x) + else: + result.kind = rkNode + var n = x + if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1] + n = n.canonValue + n.flags.incl nfIsRef + n.typ = x.typ + result.node = n var evalMacroCounter: int @@ -1423,11 +1439,18 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = inc(evalMacroCounter) if evalMacroCounter > 100: globalError(n.info, errTemplateInstantiationTooNested) + + # immediate macros can bypass any type and arity checking so we check the + # arity here too: + if sym.typ.len > n.safeLen and sym.typ.len > 1: + globalError(n.info, "got $#, but expected $# argument(s)" % [$ <n.safeLen, $ <sym.typ.len]) + setupGlobalCtx(module) var c = globalCtx c.callsite = nOrig let start = genProc(c, sym) + # c.echoCode start var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) let maxSlots = sym.offset @@ -1438,13 +1461,19 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # This is wrong for tests/reject/tind1.nim where the passed 'else' part # doesn't end up in the parameter: #InternalAssert tos.slots.len >= L + # return value: tos.slots[0].kind = rkNode tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) # setup parameters: - for i in 1 .. < min(tos.slots.len, L): - tos.slots[i].kind = rkNode - tos.slots[i].node = setupMacroParam(n.sons[i]) + for i in 1.. <sym.typ.len: + tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i]) + + let gp = sym.ast[genericParamsPos] + for i in 0 .. <gp.len: + let idx = sym.typ.len + i + tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ) + # temporary storage: #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index c06606318..3d49cb130 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -164,7 +164,8 @@ type slotTempInt, # some temporary int slotTempFloat, # some temporary float slotTempStr, # some temporary string - slotTempComplex # some complex temporary (s.node field is used) + slotTempComplex, # some complex temporary (s.node field is used) + slotTempPerm # slot is temporary but permanent (hack) PProc* = ref object blocks*: seq[TBlock] # blocks; temp data structure diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index da31eab3d..8444af7ba 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -16,7 +16,7 @@ # types that use the 'node' field; the reason is that slots are # re-used in a register based VM. Example: # -# .. code-block:: nimrod +# .. code-block:: nim # let s = a & b # no matter what, create fresh node # s = a & b # no matter what, keep the node # @@ -28,7 +28,7 @@ # this copy depends on the involved types. import - unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, + unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, trees, intsets, rodread, magicsys, options, lowerings from os import splitFile @@ -58,6 +58,7 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = if i in jumpTargets: result.addf("L$1:\n", i) let x = c.code[i] + result.add($i) let opc = opcode(x) if opc in {opcConv, opcCast}: let y = c.code[i+1] @@ -163,7 +164,8 @@ proc getSlotKind(t: PType): TSlotKind = const HighRegisterPressure = 40 -proc getTemp(c: PCtx; typ: PType): TRegister = +proc getTemp(c: PCtx; tt: PType): TRegister = + let typ = tt.safeSkipTypes({tyStatic}) let c = c.prc # we prefer the same slot kind here for efficiency. Unfortunately for # discardable return types we may not know the desired type. This can happen @@ -188,7 +190,7 @@ proc getTemp(c: PCtx; typ: PType): TRegister = proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc - if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false + if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: c.slots[r].inUse = false proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister = # if register pressure is high, we re-use more aggressively: @@ -370,8 +372,8 @@ proc sameConstant*(a, b: PNode): bool = of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal - of nkType: result = a.typ == b.typ - of nkEmpty, nkNilLit: result = true + of nkType, nkNilLit: result = a.typ == b.typ + of nkEmpty: result = true else: if sonsLen(a) == sonsLen(b): for i in countup(0, sonsLen(a) - 1): @@ -463,7 +465,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = # from the stack c.gABx(fin, opcFinally, 0, 0) if fin.kind == nkFinally: - c.gen(fin.sons[0], dest) + c.gen(fin.sons[0]) c.clearDest(n, dest) c.gABx(fin, opcFinallyEnd, 0, 0) @@ -684,7 +686,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.gABx(n, opc, 0, genType(c, n.typ)) - c.gABx(n, opc, 0, genType(c, arg.typ)) + c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic}))) c.freeTemp(tmp) proc genCard(c: PCtx; n: PNode; dest: var TDest) = @@ -899,12 +901,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) of mEcho: unused(n, dest) - let x = c.getTempRange(n.len-1, slotTempUnknown) - for i in 1.. <n.len: - var r: TRegister = x+i-1 + let n = n[1].skipConv + let x = c.getTempRange(n.len, slotTempUnknown) + internalAssert n.kind == nkBracket + for i in 0.. <n.len: + var r: TRegister = x+i c.gen(n.sons[i], r) - c.gABC(n, opcEcho, x, n.len-1) - c.freeTempRange(x, n.len-1) + c.gABC(n, opcEcho, x, n.len) + c.freeTempRange(x, n.len) of mAppendStrCh: unused(n, dest) genBinaryStmtVar(c, n, opcAddStrCh) @@ -1072,15 +1076,18 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; c.gABC(n, opcNodeToReg, dest, dest) elif c.prc.slots[tmp].kind >= slotTempUnknown: gABC(c, n, opcAddrNode, dest, tmp) - # XXX this can still be wrong sometimes; hopefully it's only generated - # in call contexts, where it is safe + # hack ahead; in order to fix bug #1781 we mark the temporary as + # permanent, so that it's not used for anything else: + c.prc.slots[tmp].kind = slotTempPerm + # XXX this is still a hack #message(n.info, warnUser, "suspicious opcode used") else: gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) proc whichAsgnOpc(n: PNode): TOpcode = - case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind + let toSkip = abstractRange-{tyTypeDesc} + case n.typ.skipTypes(toSkip).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: opcAsgnInt of tyString, tyCString: @@ -1348,7 +1355,9 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) - of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, + of tyCString, tyString: + result = newNodeIT(nkStrLit, info, t) + of tyVar, tyPointer, tyPtr, tySequence, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef: result = newNodeIT(nkNilLit, info, t) of tyProc: @@ -1552,6 +1561,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = c.gABx(n, opcLdConst, dest, lit) of skType: genTypeLit(c, s.typ, dest) + of skGenericParam: + if c.prc.sym.kind == skMacro: + genRdVar(c, n, dest, flags) + else: + internalError(n.info, "cannot generate code for: " & s.name.s) else: internalError(n.info, "cannot generate code for: " & s.name.s) of nkCallKinds: @@ -1568,7 +1582,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genLit(c, n, dest) of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) of nkNilLit: - if not n.typ.isEmptyType: genLit(c, n, dest) + if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info), dest) else: unused(n, dest) of nkAsgn, nkFastAsgn: unused(n, dest) @@ -1608,6 +1622,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = gen(c, n.sons[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(c, n, n.sons[1], dest) + of nkObjDownConv: + genConv(c, n, n.sons[0], dest) of nkVarSection, nkLetSection: unused(n, dest) genVarSection(c, n) @@ -1643,7 +1659,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = if allowCast in c.features: genConv(c, n, n.sons[1], dest, opcCast) else: - localError(n.info, errGenerated, "VM is not allowed to 'cast'") + globalError(n.info, errGenerated, "VM is not allowed to 'cast'") else: internalError n.info, "cannot generate VM code for " & n.renderTree @@ -1681,6 +1697,14 @@ proc genParams(c: PCtx; params: PNode) = c.prc.slots[i] = (inUse: true, kind: slotFixedLet) c.prc.maxSlots = max(params.len, 1) +proc genGenericParams(c: PCtx; gp: PNode) = + var base = c.prc.maxSlots + for i in 0.. <gp.len: + var param = gp.sons[i].sym + param.position = base + i # XXX: fix this earlier; make it consistent with templates + c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet) + c.prc.maxSlots = base + gp.len + proc finalJumpTarget(c: PCtx; pc, diff: int) = internalAssert(-0x7fff < diff and diff < 0x7fff) let oldInstr = c.code[pc] @@ -1752,6 +1776,8 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = p # iterate over the parameters and allocate space for them: genParams(c, s.typ.n) + if s.kind == skMacro and s.ast[genericParamsPos].kind != nkEmpty: + genGenericParams(c, s.ast[genericParamsPos]) if tfCapturesEnv in s.typ.flags: #let env = s.ast.sons[paramsPos].lastSon.sym #assert env.position == 2 diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 068f59c71..1148bdeaf 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -24,9 +24,9 @@ type wAddr, wAnd, wAs, wAsm, wAtomic, wBind, wBlock, wBreak, wCase, wCast, wConst, - wContinue, wConverter, wDiscard, wDistinct, wDiv, wDo, + wContinue, wConverter, wDefer, wDiscard, wDistinct, wDiv, wDo, wElif, wElse, wEnd, wEnum, wExcept, wExport, - wFinally, wFor, wFrom, wGeneric, wIf, wImport, wIn, + wFinally, wFor, wFrom, wFunc, wGeneric, wIf, wImport, wIn, wInclude, wInterface, wIs, wIsnot, wIterator, wLet, wMacro, wMethod, wMixin, wMod, wNil, wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn, @@ -61,7 +61,8 @@ type wPassc, wPassl, wBorrow, wDiscardable, wFieldChecks, wWatchPoint, wSubsChar, - wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, + wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, + wInjectStmt, wExperimental, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wAsmNoStackFrame, wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks, @@ -103,9 +104,9 @@ const "addr", "and", "as", "asm", "atomic", "bind", "block", "break", "case", "cast", "const", "continue", "converter", - "discard", "distinct", "div", "do", + "defer", "discard", "distinct", "div", "do", "elif", "else", "end", "enum", "except", "export", - "finally", "for", "from", "generic", "if", + "finally", "for", "from", "func", "generic", "if", "import", "in", "include", "interface", "is", "isnot", "iterator", "let", "macro", "method", "mixin", "mod", "nil", "not", "notin", @@ -144,7 +145,7 @@ const "passc", "passl", "borrow", "discardable", "fieldchecks", "watchpoint", "subschar", "acyclic", "shallow", "unroll", "linearscanend", - "computedgoto", "injectstmt", + "computedgoto", "injectstmt", "experimental", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", "guard", "locks", |