diff options
49 files changed, 446 insertions, 319 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 2ce0afcc3..fecbefd7e 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -298,6 +298,7 @@ const sfWrittenTo* = sfBorrow # param is assigned to sfEscapes* = sfProcvar # param escapes sfBase* = sfDiscriminant + sfIsSelf* = sfOverriden # param is 'self' const # getting ready for the future expr/stmt merge @@ -458,11 +459,11 @@ type tfByCopy, # pass object/tuple by copy (C backend) tfByRef, # pass object/tuple by reference (C backend) tfIterator, # type is really an iterator, not a tyProc - tfShared, # type is 'shared' + tfPartial, # type is declared as 'partial' tfNotNil, # type cannot be 'nil' tfNeedsInit, # type constains a "not nil" constraint somewhere or some - # other type so that it requires initalization + # other type so that it requires initialization tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode tfHasMeta, # type contains "wildcard" sub-types such as generic params # or other type classes @@ -533,7 +534,7 @@ const skError* = skUnknown # type flags that are essential for type equality: - eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr} + eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr} type TMagic* = enum # symbols that require compiler magic: @@ -753,7 +754,6 @@ type TScope* = object depthLevel*: int symbols*: TStrTable - usingSyms*: seq[PNode] parent*: PScope PScope* = ref TScope diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 1a70875d4..4772009b4 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -635,7 +635,7 @@ proc reallySameIdent(a, b: string): bool {.inline.} = else: result = true -proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = +proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.discardable.} = # returns true if n is already in the string table: # It is essential that `n` is written nevertheless! # This way the newest redefinition is picked by the semantic analyses! @@ -654,7 +654,8 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = replaceSlot = h h = nextTry(h, high(t.data)) if replaceSlot >= 0: - t.data[replaceSlot] = n # overwrite it with newer definition! + if not onConflictKeepOld: + t.data[replaceSlot] = n # overwrite it with newer definition! return true # found it elif mustRehash(len(t.data), t.counter): strTableEnlarge(t) diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 9afe4ab10..089bce302 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -158,7 +158,6 @@ proc hashType(c: var MD5Context, t: PType) = if tfThread in t.flags: c &= ".thread" else: for i in 0.. <t.len: c.hashType(t.sons[i]) - if tfShared in t.flags: c &= "shared" if tfNotNil in t.flags: c &= "not nil" proc canonConst(n: PNode): TUid = @@ -276,7 +275,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = return # we need no surrounding [] here because the type is in a line of its own if t.kind == tyForward: internalError("encodeType: tyForward") - # for the new rodfile viewer we use a preceeding [ so that the data section + # for the new rodfile viewer we use a preceding [ so that the data section # can easily be disambiguated: add(result, '[') encodeVInt(ord(t.kind), result) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8f1f8a832..d6b546d8b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -192,7 +192,7 @@ proc mangleName(s: PSym; target: TTarget): Rope = x.add("HEX" & toHex(ord(c), 2)) inc i result = rope(x) - if s.name.s != "this": + if s.name.s != "this" and s.kind != skField: add(result, "_") add(result, rope(s.id)) s.loc.r = result @@ -891,10 +891,12 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = r.res = "$1['$2']" % [r.res, f.loc.r] r.kind = resExpr +proc genAddr(p: PProc, n: PNode, r: var TCompRes) + proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = let m = if n.kind == nkHiddenAddr: n.sons[0] else: n internalAssert m.kind == nkCheckedFieldExpr - genFieldAddr(p, m.sons[0], r) # XXX + genAddr(p, m, r) # XXX proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = genFieldAccess(p, n.sons[0], r) # XXX diff --git a/compiler/lexer.nim b/compiler/lexer.nim index fc43f8d6a..69a0fea2a 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -44,7 +44,8 @@ type tkLet, tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, - tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShl, tkShr, tkStatic, + tkProc, tkPtr, tkRaise, tkRef, tkReturn, + tkShl, tkShr, tkStatic, tkTemplate, tkTry, tkTuple, tkType, tkUsing, tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor, diff --git a/compiler/parser.nim b/compiler/parser.nim index c4681a5cd..f22177ac1 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -804,20 +804,24 @@ proc parsePragma(p: var TParser): PNode = else: parMessage(p, errTokenExpected, ".}") dec p.inPragma -proc identVis(p: var TParser): PNode = +proc identVis(p: var TParser; allowDot=false): PNode = #| identVis = symbol opr? # postfix position + #| identVisDot = symbol '.' optInd symbol opr? var a = parseSymbol(p) if p.tok.tokType == tkOpr: result = newNodeP(nkPostfix, p) addSon(result, newIdentNodeP(p.tok.ident, p)) addSon(result, a) getTok(p) + elif p.tok.tokType == tkDot and allowDot: + result = dotExpr(p, a) else: result = a -proc identWithPragma(p: var TParser): PNode = +proc identWithPragma(p: var TParser; allowDot=false): PNode = #| identWithPragma = identVis pragma? - var a = identVis(p) + #| identWithPragmaDot = identVisDot pragma? + var a = identVis(p, allowDot) if p.tok.tokType == tkCurlyDotLe: result = newNodeP(nkPragmaExpr, p) addSon(result, a) @@ -1803,10 +1807,11 @@ proc parseTypeClass(p: var TParser): PNode = addSon(result, parseStmt(p)) proc parseTypeDef(p: var TParser): PNode = - #| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux + #| + #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux #| indAndComment? result = newNodeP(nkTypeDef, p) - addSon(result, identWithPragma(p)) + addSon(result, identWithPragma(p, allowDot=true)) if p.tok.tokType == tkBracketLe and p.validInd: addSon(result, parseGenericParamList(p)) else: @@ -1903,7 +1908,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = #| | 'converter' routine #| | 'type' section(typeDef) #| | 'const' section(constant) - #| | ('let' | 'var') section(variable) + #| | ('let' | 'var' | 'using') section(variable) #| | bindStmt | mixinStmt) #| / simpleStmt case p.tok.tokType @@ -1940,7 +1945,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkVar: result = parseSection(p, nkVarSection, parseVariable) of tkBind: result = parseBind(p, nkBindStmt) of tkMixin: result = parseBind(p, nkMixinStmt) - of tkUsing: result = parseBind(p, nkUsingStmt) + of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable) else: result = simpleStmt(p) proc parseStmt(p: var TParser): PNode = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index f10d552a1..2280ef712 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -46,7 +46,7 @@ const wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto, - wInjectStmt, wDeprecated, wExperimental} + wInjectStmt, wDeprecated, wExperimental, wThis} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, @@ -55,7 +55,7 @@ const wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wBorrow, wGcSafe, wExportNims} + wBorrow, wGcSafe, wExportNims, wPartial} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError, wGuard, wBitsize} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -835,6 +835,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, noVal(it) if sym.kind != skType or sym.typ == nil: invalidPragma(it) else: incl(sym.typ.flags, tfByCopy) + of wPartial: + noVal(it) + if sym.kind != skType or sym.typ == nil: invalidPragma(it) + else: + incl(sym.typ.flags, tfPartial) + # .partial types can only work with dead code elimination + # to prevent the codegen from doing anything before we compiled + # the whole program: + incl gGlobalOptions, optDeadCodeElim of wInject, wGensym: # We check for errors, but do nothing with these pragmas otherwise # as they are handled directly in 'evalTemplate'. @@ -875,6 +884,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, c.module.flags.incl sfExperimental else: localError(it.info, "'experimental' pragma only valid as toplevel statement") + of wThis: + if it.kind == nkExprColonExpr: + c.selfName = considerQuotedIdent(it[1]) + else: + c.selfName = getIdent("self") of wNoRewrite: noVal(it) of wBase: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index d63cba4be..12852ba3d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -463,6 +463,9 @@ proc lsub(n: PNode): int = of nkVarSection, nkLetSection: if sonsLen(n) > 1: result = MaxLineLen + 1 else: result = lsons(n) + len("var_") + of nkUsingStmt: + if sonsLen(n) > 1: result = MaxLineLen + 1 + else: result = lsons(n) + len("using_") of nkReturnStmt: result = lsub(n.sons[0]) + len("return_") of nkRaiseStmt: result = lsub(n.sons[0]) + len("raise_") of nkYieldStmt: result = lsub(n.sons[0]) + len("yield_") @@ -1173,11 +1176,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = initContext(a) incl(a.flags, rfInConstExpr) gsection(g, n, a, tkConst, "const") - of nkVarSection, nkLetSection: + of nkVarSection, nkLetSection, nkUsingStmt: var L = sonsLen(n) if L == 0: return if n.kind == nkVarSection: putWithSpace(g, tkVar, "var") - else: putWithSpace(g, tkLet, "let") + elif n.kind == nkLetSection: putWithSpace(g, tkLet, "let") + else: putWithSpace(g, tkUsing, "using") if L > 1: gcoms(g) indentNL(g) diff --git a/compiler/sem.nim b/compiler/sem.nim index cddd763ce..e09d49f88 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -191,7 +191,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym - # identifier with visability + # identifier with visibility proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym proc semStmtScope(c: PContext, n: PNode): PNode diff --git a/compiler/semcall.nim b/compiler/semcall.nim index eba1059ef..916d897c9 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -50,7 +50,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, var syms: seq[tuple[a: PSym, b: int]] = @[] while symx != nil: - if symx.kind in filter: syms.add((symx, o.lastOverloadScope)) + if symx.kind in filter: + syms.add((symx, o.lastOverloadScope)) symx = nextOverloadIter(o, c, headSymbol) if syms.len == 0: return @@ -63,7 +64,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, let sym = syms[i][0] determineType(c, sym) initCandidate(c, z, sym, initialBinding, syms[i][1]) - z.calleeSym = sym #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: # gDebug = true @@ -138,11 +138,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = else: localError(n.info, errGenerated, result) -proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) = - for scope in walkScopes(c.currentScope): - if scope.usingSyms != nil: - for s in scope.usingSyms: usedSyms.safeAdd(s) - proc resolveOverloads(c: PContext, n, orig: PNode, filter: TSymKinds; errors: var CandidateErrors): TCandidate = @@ -156,31 +151,30 @@ proc resolveOverloads(c: PContext, n, orig: PNode, else: initialBinding = nil - var usedSyms: seq[PNode] - - template pickBest(headSymbol: expr) = + template pickBest(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, filter, result, alt, errors) - gatherUsedSyms(c, usedSyms) - if usedSyms != nil: - var hiddenArg = if usedSyms.len > 1: newNode(nkClosedSymChoice, n.info, usedSyms) - else: usedSyms[0] - - n.sons.insert(hiddenArg, 1) - orig.sons.insert(hiddenArg, 1) - - pickBest(f) - - if result.state != csMatch: - n.sons.delete(1) - orig.sons.delete(1) - else: return pickBest(f) let overloadsState = result.state if overloadsState != csMatch: + if c.p != nil and c.p.selfSym != nil: + # we need to enforce semchecking of selfSym again because it + # might need auto-deref: + var hiddenArg = newSymNode(c.p.selfSym) + hiddenArg.typ = nil + n.sons.insert(hiddenArg, 1) + orig.sons.insert(hiddenArg, 1) + + pickBest(f) + + if result.state != csMatch: + n.sons.delete(1) + orig.sons.delete(1) + else: return + if nfDotField in n.flags: internalAssert f.kind == nkIdent and n.sonsLen >= 2 let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 656bfc449..a13f2c232 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -30,6 +30,7 @@ type # statements owner*: PSym # the symbol this context belongs to resultSym*: PSym # the result symbol (if we are in a proc) + selfSym*: PSym # the 'self' symbol (if available) nestedLoopCounter*: int # whether we are in a loop or not nestedBlockCounter*: int # whether we are in a block or not inTryStmt*: int # whether we are in a try statement; works also @@ -103,7 +104,8 @@ type inParallelStmt*: int instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo; op: TTypeAttachedOp): PSym {.nimcall.} - + selfName*: PIdent + signatures*: TStrTable proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s @@ -154,16 +156,6 @@ proc popOwner() = proc lastOptionEntry(c: PContext): POptionEntry = result = POptionEntry(c.optionStack.tail) -proc pushProcCon*(c: PContext, owner: PSym) {.inline.} = - if owner == nil: - internalError("owner is nil") - return - var x: PProcCon - new(x) - x.owner = owner - x.next = c.p - c.p = x - proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc newOptionEntry(): POptionEntry = @@ -187,6 +179,8 @@ proc newContext(module: PSym): PContext = initStrTable(result.userPragmas) result.generics = @[] result.unknownIdents = initIntSet() + initStrTable(result.signatures) + proc inclSym(sq: var TSymSeq, s: PSym) = var L = len(sq) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 87d7764a2..16b4ee479 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -77,88 +77,6 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = result.typ = s.typ result.info = n.info -proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = - case s.kind - of skConst: - markUsed(n.info, s) - styleCheckUse(n.info, s) - case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind - of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, - tyTuple, tySet, tyUInt..tyUInt64: - if s.magic == mNone: result = inlineConst(n, s) - else: result = newSymNode(s, n.info) - of tyArrayConstr, tySequence: - # Consider:: - # const x = [] - # proc p(a: openarray[int]) - # proc q(a: openarray[char]) - # p(x) - # q(x) - # - # It is clear that ``[]`` means two totally different things. Thus, we - # copy `x`'s AST into each context, so that the type fixup phase can - # deal with two different ``[]``. - if s.ast.len == 0: result = inlineConst(n, s) - else: result = newSymNode(s, n.info) - else: - result = newSymNode(s, n.info) - of skMacro: result = semMacroExpr(c, n, n, s, flags) - of skTemplate: result = semTemplateExpr(c, n, s, flags) - of skParam: - markUsed(n.info, s) - styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: - # XXX see the hack in sigmatch.nim ... - return s.typ.n - elif sfGenSym in s.flags: - if c.p.wasForwarded: - # gensym'ed parameters that nevertheless have been forward declared - # need a special fixup: - let realParam = c.p.owner.typ.n[s.position+1] - internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam - return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info) - elif c.p.owner.kind == skMacro: - # gensym'ed macro parameters need a similar hack (see bug #1944): - var u = searchInScopes(c, s.name) - internalAssert u != nil and u.kind == skParam and u.owner == s.owner - return newSymNode(u, n.info) - result = newSymNode(s, n.info) - of skVar, skLet, skResult, skForVar: - if s.magic == mNimvm: - localError(n.info, "illegal context for 'nimvm' magic") - - markUsed(n.info, s) - styleCheckUse(n.info, s) - # if a proc accesses a global variable, it is not side effect free: - if sfGlobal in s.flags: - incl(c.p.owner.flags, sfSideEffect) - result = newSymNode(s, n.info) - # We cannot check for access to outer vars for example because it's still - # not sure the symbol really ends up being used: - # var len = 0 # but won't be called - # genericThatUsesLen(x) # marked as taking a closure? - of skGenericParam: - styleCheckUse(n.info, s) - if s.typ.kind == tyStatic: - result = newSymNode(s, n.info) - result.typ = s.typ - elif s.ast != nil: - result = semExpr(c, s.ast) - else: - n.typ = s.typ - return n - of skType: - markUsed(n.info, s) - styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: - return s.typ.n - result = newSymNode(s, n.info) - result.typ = makeTypeDesc(c, s.typ) - else: - markUsed(n.info, s) - styleCheckUse(n.info, s) - result = newSymNode(s, n.info) - type TConvStatus = enum convOK, @@ -1015,6 +933,116 @@ proc readTypeParameter(c: PContext, typ: PType, return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) #echo "came here: returned nil" +proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + case s.kind + of skConst: + markUsed(n.info, s) + styleCheckUse(n.info, s) + case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind + of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, + tyTuple, tySet, tyUInt..tyUInt64: + if s.magic == mNone: result = inlineConst(n, s) + else: result = newSymNode(s, n.info) + of tyArrayConstr, tySequence: + # Consider:: + # const x = [] + # proc p(a: openarray[int]) + # proc q(a: openarray[char]) + # p(x) + # q(x) + # + # It is clear that ``[]`` means two totally different things. Thus, we + # copy `x`'s AST into each context, so that the type fixup phase can + # deal with two different ``[]``. + if s.ast.len == 0: result = inlineConst(n, s) + else: result = newSymNode(s, n.info) + else: + result = newSymNode(s, n.info) + of skMacro: result = semMacroExpr(c, n, n, s, flags) + of skTemplate: result = semTemplateExpr(c, n, s, flags) + of skParam: + markUsed(n.info, s) + styleCheckUse(n.info, s) + if s.typ.kind == tyStatic and s.typ.n != nil: + # XXX see the hack in sigmatch.nim ... + return s.typ.n + elif sfGenSym in s.flags: + if c.p.wasForwarded: + # gensym'ed parameters that nevertheless have been forward declared + # need a special fixup: + let realParam = c.p.owner.typ.n[s.position+1] + internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam + return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info) + elif c.p.owner.kind == skMacro: + # gensym'ed macro parameters need a similar hack (see bug #1944): + var u = searchInScopes(c, s.name) + internalAssert u != nil and u.kind == skParam and u.owner == s.owner + return newSymNode(u, n.info) + result = newSymNode(s, n.info) + of skVar, skLet, skResult, skForVar: + if s.magic == mNimvm: + localError(n.info, "illegal context for 'nimvm' magic") + + markUsed(n.info, s) + styleCheckUse(n.info, s) + # if a proc accesses a global variable, it is not side effect free: + if sfGlobal in s.flags: + incl(c.p.owner.flags, sfSideEffect) + result = newSymNode(s, n.info) + # We cannot check for access to outer vars for example because it's still + # not sure the symbol really ends up being used: + # var len = 0 # but won't be called + # genericThatUsesLen(x) # marked as taking a closure? + of skGenericParam: + styleCheckUse(n.info, s) + if s.typ.kind == tyStatic: + result = newSymNode(s, n.info) + result.typ = s.typ + elif s.ast != nil: + result = semExpr(c, s.ast) + else: + n.typ = s.typ + return n + of skType: + markUsed(n.info, s) + styleCheckUse(n.info, s) + if s.typ.kind == tyStatic and s.typ.n != nil: + return s.typ.n + result = newSymNode(s, n.info) + result.typ = makeTypeDesc(c, s.typ) + of skField: + if c.p != nil and c.p.selfSym != nil: + var ty = skipTypes(c.p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) + var check: PNode = nil + if ty.kind == tyObject: + while true: + check = nil + let f = lookupInRecordAndBuildCheck(c, n, ty.n, s.name, check) + if f != nil and fieldVisible(c, f): + # is the access to a public field or in the same module or in a friend? + doAssert f == s + markUsed(n.info, f) + styleCheckUse(n.info, f) + result = newNodeIT(nkDotExpr, n.info, f.typ) + result.add makeDeref(newSymNode(c.p.selfSym)) + result.add newSymNode(f) # we now have the correct field + if check != nil: + check.sons[0] = result + check.typ = result.typ + result = check + return result + if ty.sons[0] == nil: break + ty = skipTypes(ty.sons[0], {tyGenericInst}) + # old code, not sure if it's live code: + markUsed(n.info, s) + styleCheckUse(n.info, s) + result = newSymNode(s, n.info) + else: + markUsed(n.info, s) + styleCheckUse(n.info, s) + result = newSymNode(s, n.info) + proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if it's not a built-in field access checkSonsLen(n, 2) @@ -1528,24 +1556,6 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo, result = newSym(kind, idAnon, owner, info) result.flags = {sfGenSym} -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: - case usedSym.sym.kind - of skLocalVars + {skConst}: - c.currentScope.usingSyms.safeAdd(usedSym) - continue - of skProcKinds: - addDeclAt(c.currentScope, usedSym.sym) - continue - else: discard - - localError(e.info, errUsingNoSymbol, e.renderTree) - proc semExpandToAst(c: PContext, n: PNode): PNode = var macroCall = n[1] var expandedSym = expectMacroOrTemplateCall(c, macroCall) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 2c767ffc6..4a45dee9d 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -10,6 +10,47 @@ # This module implements the instantiation of generic procs. # included from sem.nim +proc addObjFieldsToLocalScope(c: PContext; n: PNode) = + template rec(n) = addObjFieldsToLocalScope(c, n) + case n.kind + of nkRecList: + for i in countup(0, len(n)-1): + rec n[i] + of nkRecCase: + if n.len > 0: rec n.sons[0] + for i in countup(1, len(n)-1): + if n[i].kind in {nkOfBranch, nkElse}: rec lastSon(n[i]) + of nkSym: + let f = n.sym + if f.kind == skField and fieldVisible(c, f): + c.currentScope.symbols.strTableIncl(f, onConflictKeepOld=true) + incl(f.flags, sfUsed) + # it is not an error to shadow fields via parameters + else: discard + +proc rawPushProcCon(c: PContext, owner: PSym) = + var x: PProcCon + new(x) + x.owner = owner + x.next = c.p + c.p = x + +proc rawHandleSelf(c: PContext; owner: PSym) = + if c.selfName != nil and owner.kind in {skProc, skMethod, skConverter, skIterator, skMacro} and owner.typ != nil: + let params = owner.typ.n + if params.len > 1: + let arg = params[1].sym + if arg.name.id == c.selfName.id: + c.p.selfSym = arg + arg.flags.incl sfIsSelf + let t = c.p.selfSym.typ.skipTypes(abstractPtrs) + if t.kind == tyObject: + addObjFieldsToLocalScope(c, t.n) + +proc pushProcCon*(c: PContext; owner: PSym) = + rawPushProcCon(c, owner) + rawHandleSelf(c, owner) + iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = internalAssert n.kind == nkGenericParams for i, a in n.pairs: @@ -248,7 +289,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, addDecl(c, s) entry.concreteTypes[i] = s.typ inc i - pushProcCon(c, result) + rawPushProcCon(c, result) instantiateProcType(c, pt, result, info) for j in 1 .. result.typ.len-1: entry.concreteTypes[i] = result.typ.sons[j] @@ -263,6 +304,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # a ``compiles`` context but this is the lesser evil. See # bug #1055 (tevilcompiles). #if c.compilesContextId == 0: + rawHandleSelf(c, result) entry.compilesId = c.compilesContextId fn.procInstCache.safeAdd(entry) c.generics.add(makeInstPair(fn, entry)) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index edcf079fa..5d16f2fba 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -386,6 +386,30 @@ proc isDiscardUnderscore(v: PSym): bool = v.flags.incl(sfGenSym) result = true +proc semUsing(c: PContext; n: PNode): PNode = + result = ast.emptyNode + if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using") + if not experimentalMode(c): + localError(n.info, "use the {.experimental.} pragma to enable 'using'") + for i in countup(0, sonsLen(n)-1): + var a = n.sons[i] + if gCmd == cmdIdeTools: suggestStmt(c, a) + if a.kind == nkCommentStmt: continue + if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a) + checkMinSonsLen(a, 3) + var length = sonsLen(a) + if a.sons[length-2].kind != nkEmpty: + let typ = semTypeNode(c, a.sons[length-2], nil) + for j in countup(0, length-3): + let v = semIdentDef(c, a.sons[j], skParam) + v.typ = typ + strTableIncl(c.signatures, v) + else: + localError(a.info, "'using' section must have a type") + var def: PNode + if a.sons[length-1].kind != nkEmpty: + localError(a.info, "'using' sections cannot contain assignments") + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) @@ -640,13 +664,20 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = if a.kind == nkCommentStmt: continue if a.kind != nkTypeDef: illFormedAst(a) checkSonsLen(a, 3) - var s = semIdentDef(c, a.sons[0], skType) - s.typ = newTypeS(tyForward, c) - s.typ.sym = s # process pragmas: - if a.sons[0].kind == nkPragmaExpr: - pragma(c, s, a.sons[0].sons[1], typePragmas) - # add it here, so that recursive types are possible: - if sfGenSym notin s.flags: addInterfaceDecl(c, s) + let name = a.sons[0] + var s: PSym + if name.kind == nkDotExpr: + s = qualifiedLookUp(c, name) + if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags: + localError(name.info, "only .partial objects can be extended") + else: + s = semIdentDef(c, name, skType) + s.typ = newTypeS(tyForward, c) + s.typ.sym = s # process pragmas: + if name.kind == nkPragmaExpr: + pragma(c, s, name.sons[1], typePragmas) + # add it here, so that recursive types are possible: + if sfGenSym notin s.flags: addInterfaceDecl(c, s) a.sons[0] = newSymNode(s) proc typeSectionRightSidePass(c: PContext, n: PNode) = @@ -655,8 +686,9 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) - if (a.sons[0].kind != nkSym): illFormedAst(a) - var s = a.sons[0].sym + let name = a.sons[0] + if (name.kind != nkSym): illFormedAst(a) + var s = name.sym if s.magic == mNone and a.sons[2].kind == nkEmpty: localError(a.info, errImplOfXexpected, s.name.s) if s.magic != mNone: processMagicType(c, s) @@ -1206,9 +1238,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # Macros and Templates can have generic parameters, but they are # only used for overload resolution (there is no instantiation of # the symbol, so we must process the body now) + pushProcCon(c, s) if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics: if not usePseudoGenerics: paramsTypeCheck(c, s.typ) - pushProcCon(c, s) + c.p.wasForwarded = proto != nil maybeAddResult(c, s, n) if sfImportc notin s.flags: @@ -1217,7 +1250,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': n.sons[bodyPos] = transformBody(c.module, semBody, s) - popProcCon(c) else: if s.typ.sons[0] != nil and kind != skIterator: addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) @@ -1228,6 +1260,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfImportc in s.flags: # so we just ignore the body after semantic checking for importc: n.sons[bodyPos] = ast.emptyNode + popProcCon(c) else: if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s) if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index ac425ba15..62d02fe10 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -673,7 +673,11 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") result = newOrPrevType(tyObject, prev, c) rawAddSon(result, base) - result.n = newNodeI(nkRecList, n.info) + if result.n.isNil: + result.n = newNodeI(nkRecList, n.info) + else: + # partial object so add things to the check + addInheritedFields(c, check, pos, result) semRecordNodeAux(c, n.sons[2], check, pos, result.n, result) if n.sons[0].kind != nkEmpty: # dummy symbol for `pragma`: @@ -934,14 +938,18 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, def = fitNode(c, typ, def) if not hasType and not hasDefault: if isType: localError(a.info, "':' expected") - let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything - if tdef == tyAnything: - message(a.info, warnTypelessParam, renderTree(n)) - typ = newTypeS(tdef, c) - - if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue + if kind in {skTemplate, skMacro}: + typ = newTypeS(tyExpr, c) + elif skipTypes(typ, {tyGenericInst}).kind == tyEmpty: + continue for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) + if not hasType and not hasDefault and kind notin {skTemplate, skMacro}: + let param = strTableGet(c.signatures, arg.name) + if param != nil: typ = param.typ + else: + localError(a.info, "typeless parameters are obsolete") + typ = errorType(c) let lifted = liftParamType(c, kind, genericParams, typ, arg.name.s, arg.info) let finalType = if lifted != nil: lifted else: typ.skipIntLit @@ -1307,11 +1315,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkType: result = n.typ of nkStmtListType: result = semStmtListType(c, n, prev) of nkBlockType: result = semBlockType(c, n, prev) - of nkSharedTy: - checkSonsLen(n, 1) - result = semTypeNode(c, n.sons[0], prev) - result = freshType(result, prev) - result.flags.incl(tfShared) else: localError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) @@ -1387,15 +1390,6 @@ proc processMagicType(c: PContext, m: PSym) = rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: incl m.typ.flags, tfTriggersCompileTime - of mShared: - setMagicType(m, tyObject, 0) - m.typ.n = newNodeI(nkRecList, m.info) - incl m.typ.flags, tfShared - of mGuarded: - setMagicType(m, tyObject, 0) - m.typ.n = newNodeI(nkRecList, m.info) - incl m.typ.flags, tfShared - rawAddSon(m.typ, sysTypeFromName"shared") else: localError(m.info, errTypeExpected) proc semGenericConstraints(c: PContext, x: PType): PType = diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index a7f28feec..d20704a2a 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -14,22 +14,11 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer const tfInstClearedFlags = {tfHasMeta} -proc sharedPtrCheck(info: TLineInfo, t: PType) = - if t.kind == tyPtr and t.len > 1: - if t.sons[0].sym.magic == mShared: - incl(t.flags, tfShared) - #if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded) - if tfHasGCedMem in t.flags or t.isGCedMem: - localError(info, errGenerated, - "shared memory may not refer to GC'ed thread local memory") - proc checkPartialConstructedType(info: TLineInfo, t: PType) = if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: localError(info, errInvalidPragmaX, "acyclic") elif t.kind == tyVar and t.sons[0].kind == tyVar: localError(info, errVarVarTypeNotAllowed) - else: - sharedPtrCheck(info, t) proc checkConstructedType*(info: TLineInfo, typ: PType) = var t = typ.skipTypes({tyDistinct}) @@ -40,8 +29,6 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) = localError(info, errVarVarTypeNotAllowed) elif computeSize(t) == szIllegalRecursion: localError(info, errIllegalRecursionInTypeX, typeToString(t)) - else: - sharedPtrCheck(info, t) when false: if t.kind == tyObject and t.sons[0] != nil: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 96df0c5c6..ceadc2fea 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -37,6 +37,7 @@ type # is this a top-level symbol or a nested proc? call*: PNode # modified call bindings*: TIdTable # maps types to types + magic*: TMagic # magic of operation baseTypeMatch: bool # needed for conversions from T to openarray[T] # for example fauxMatch*: TTypeKind # the match was successful only due to the use @@ -114,6 +115,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, c.calleeScope = 1 else: c.calleeScope = calleeScope + c.magic = c.calleeSym.magic initIdTable(c.bindings) c.errors = nil if binding != nil and callee.kind in routineKinds: @@ -1691,7 +1693,7 @@ proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) = matchesAux(c, n, nOrig, m, marker) proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = - if m.calleeSym != nil and m.calleeSym.magic in {mArrGet, mArrPut}: + if m.magic in {mArrGet, mArrPut}: m.state = csMatch m.call = n return diff --git a/compiler/transf.nim b/compiler/transf.nim index b2bbdcec3..a4a15ea4a 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -12,7 +12,7 @@ # # * inlines iterators # * inlines constants -# * performes constant folding +# * performs constant folding # * converts "continue" to "break"; disambiguates "break" # * introduces method dispatchers # * performs lambda lifting for closure support diff --git a/compiler/types.nim b/compiler/types.nim index 71ab84022..9aa991086 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -412,7 +412,6 @@ const const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg} proc addTypeFlags(name: var string, typ: PType) {.inline.} = - if tfShared in typ.flags: name = "shared " & name if tfNotNil in typ.flags: name.add(" not nil") proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 0a0534118..3e0e05a94 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -66,6 +66,7 @@ type wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wAsmNoStackFrame, wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks, + wPartial, wAuto, wBool, wCatch, wChar, wClass, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, @@ -151,7 +152,7 @@ const "computedgoto", "injectstmt", "experimental", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", - "guard", "locks", + "guard", "locks", "partial", "auto", "bool", "catch", "char", "class", "const_cast", "default", "delete", "double", diff --git a/doc/grammar.txt b/doc/grammar.txt index d967bf938..47f739174 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -67,7 +67,9 @@ ifExpr = 'if' condExpr whenExpr = 'when' condExpr pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}') identVis = symbol opr? # postfix position +identVisDot = symbol '.' optInd symbol opr? identWithPragma = identVis pragma? +identWithPragmaDot = identVisDot pragma? declColonEquals = identWithPragma (comma identWithPragma)* comma? (':' optInd typeDesc)? ('=' optInd expr)? identColonEquals = ident (comma ident)* comma? @@ -171,7 +173,7 @@ object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart typeClassParam = ('var' | 'out')? symbol typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? &IND{>} stmt -typeDef = identWithPragma genericParamList? '=' optInd typeDefAux +typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux indAndComment? varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr variable = (varTuple / identColonEquals) indAndComment diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt index 83852b1ff..4f8d6e935 100644 --- a/doc/manual/stmts.txt +++ b/doc/manual/stmts.txt @@ -547,55 +547,36 @@ Instead of: Using statement --------------- -**Warning**: The ``using`` statement is highly experimental and has to be +**Warning**: The ``using`` statement is experimental and has to be explicitly enabled with the `experimental`:idx: pragma or command line option! -The using statement provides syntactic convenience for procs that -heavily use a single contextual parameter. When applied to a variable or a -constant, it will instruct Nim to automatically consider the used symbol as -a hidden leading parameter for any procedure calls, following the using -statement in the current scope. Thus, it behaves much like the hidden `this` -parameter available in some object-oriented programming languages. +The using statement provides syntactic convenience in modules where +the same parameter names and types are used over and over. Instead of: .. code-block:: nim + proc foo(c: Context; n: Node) = ... + proc bar(c: Context; n: Node, counter: int) = ... + proc baz(c: Context; n: Node) = ... - var s = socket() - using s - - connect(host, port) - send(data) - - while true: - let line = readLine(timeout) - ... - - -When applied to a callable symbol, it brings the designated symbol in the -current scope. Thus, it can be used to disambiguate between imported symbols -from different modules having the same name. +One can tell the compiler about the convention that a parameter of +name ``c`` should default to type ``Context``, ``n`` should default to +``Node`` etc.: .. code-block:: nim - import windows, sdl - using sdl.SetTimer + {.experimental.} + using + c: Context + n: Node + counter: int -Note that ``using`` only *adds* to the current context, it doesn't remove or -replace, **neither** does it create a new scope. What this means is that if one -applies this to multiple variables the compiler will find conflicts in what -variable to use: + proc foo(c, n) = ... + proc bar(c, n, counter) = ... + proc baz(c, n) = ... -.. code-block:: nim - var a, b = "kill it" - using a - add(" with fire") - using b - add(" with water") - echo a - echo b -When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could -be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or -is ambiguous)``. To solve this one would need to nest ``using`` with a -``block`` statement so as to control the reach of the ``using`` statement. +The ``using`` section uses the same indentation based grouping syntax as +a ``var`` or ``let``` section. + If expression ------------- diff --git a/lib/core/macros.nim b/lib/core/macros.nim index eda793620..4522e0fc6 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -71,7 +71,12 @@ type nnkEnumTy, nnkEnumFieldDef, nnkArglist, nnkPattern - nnkReturnToken + nnkReturnToken, + nnkClosure, + nnkGotoState, + nnkState, + nnkBreakState + NimNodeKinds* = set[NimNodeKind] NimTypeKind* = enum ntyNone, ntyBool, ntyChar, ntyEmpty, diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 10700b59b..c8f690461 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -1,6 +1,6 @@ # # Nim's Runtime Library -# (c) Copyright 2015 Nim Contributers +# (c) Copyright 2015 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -508,7 +508,7 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R ## Variants: ## ## - ``proc findAll(...)`` returns a ``seq[string]`` - # see pcredemo for explaination + # see pcredemo for explanation let matchesCrLf = pattern.matchesCrLf() let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and pcre.UTF8) > 0u32 diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 2da14c363..c5b1a21e9 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1123,9 +1123,9 @@ else: proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".} - proc getVarType(x): JsonNodeKind = + proc getVarType(x: JSObject): JsonNodeKind = result = JNull - proc getProtoName(y): cstring + proc getProtoName(y: JSObject): cstring {.importc: "Object.prototype.toString.call".} case $getProtoName(x) # TODO: Implicit returns fail here. of "[object Array]": return JArray @@ -1216,23 +1216,27 @@ when false: # To get that we shall use, obj["json"] when isMainModule: - var parsed = parseFile("tests/testdata/jsontest.json") - var parsed2 = parseFile("tests/testdata/jsontest2.json") + when not defined(js): + var parsed = parseFile("tests/testdata/jsontest.json") - try: - discard parsed["key2"][12123] - assert(false) - except IndexError: assert(true) + try: + discard parsed["key2"][12123] + doAssert(false) + except IndexError: doAssert(true) + + var parsed2 = parseFile("tests/testdata/jsontest2.json") + doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}""" # nil passthrough - assert(testJson{"doesnt_exist"}{"anything"}.isNil) + doAssert(testJson{"doesnt_exist"}{"anything"}.isNil) testJson{["e", "f"]} = %true - assert(testJson["e"]["f"].bval) + doAssert(testJson["e"]["f"].bval) # make sure UTF-16 decoding works. - assert(testJson["c"].str == "🎃") - assert(testJson["d"].str == "æ") + when not defined(js): # TODO: The following line asserts in JS + doAssert(testJson["c"].str == "🎃") + doAssert(testJson["d"].str == "æ") # make sure no memory leek when parsing invalid string let startMemory = getOccupiedMem() @@ -1242,41 +1246,43 @@ when isMainModule: except: discard # memory diff should less than 2M - assert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024) + doAssert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024) # test `$` let stringified = $testJson let parsedAgain = parseJson(stringified) - assert(parsedAgain["b"].str == "asd") + doAssert(parsedAgain["b"].str == "asd") + + parsedAgain["abc"] = %5 + doAssert parsedAgain["abc"].num == 5 # Bounds checking try: let a = testJson["a"][9] - assert(false, "EInvalidIndex not thrown") + doAssert(false, "EInvalidIndex not thrown") except IndexError: discard try: let a = testJson["a"][-1] - assert(false, "EInvalidIndex not thrown") + doAssert(false, "EInvalidIndex not thrown") except IndexError: discard try: - assert(testJson["a"][0].num == 1, "Index doesn't correspond to its value") + doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value") except: - assert(false, "EInvalidIndex thrown for valid index") + doAssert(false, "EInvalidIndex thrown for valid index") - assert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}") - assert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") - assert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") - assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") - assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") - assert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found") - assert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil") + doAssert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}") + doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") + doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") + doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") + doAssert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found") + doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil") # Generator: var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}] - assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + doAssert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] var j2 = %* [ @@ -1289,7 +1295,7 @@ when isMainModule: "age": 31 } ] - assert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + doAssert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] var name = "John" let herAge = 30 @@ -1303,4 +1309,4 @@ when isMainModule: , "age": hisAge } ] - assert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + doAssert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 346b656a0..dbb78c4de 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -616,7 +616,7 @@ proc readIntoBuf(socket: Socket, flags: int32): int = else: result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags) if result < 0: - # Save it in case it gets reset (the Nim codegen occassionally may call + # Save it in case it gets reset (the Nim codegen occasionally may call # Win API functions which reset it). socket.lastError = osLastError() if result <= 0: diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index f680d4fef..9bcac0a50 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -288,19 +288,19 @@ proc rawGetTok(c: var CfgParser, tok: var Token) = else: getSymbol(c, tok) proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = - ## returns a properly formated error message containing current line and + ## returns a properly formatted error message containing current line and ## column information. result = `%`("$1($2, $3) Error: $4", [c.filename, $getLine(c), $getColumn(c), msg]) proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = - ## returns a properly formated warning message containing current line and + ## returns a properly formatted warning message containing current line and ## column information. result = `%`("$1($2, $3) Warning: $4", [c.filename, $getLine(c), $getColumn(c), msg]) proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} = - ## returns a properly formated warning message containing that + ## returns a properly formatted warning message containing that ## an entry is ignored. case e.kind of cfgSectionStart: result = c.warningStr("section ignored: " & e.section) diff --git a/lib/pure/poly.nim b/lib/pure/poly.nim index b20e9f9d0..e286c5d17 100644 --- a/lib/pure/poly.nim +++ b/lib/pure/poly.nim @@ -344,7 +344,7 @@ proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[ ## `tol` is the tolerance used to break searching for each root when reached. ## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root ## and is used to find roots for curves that only 'touch' the x-axis. - ## `mergetol` is the tolerance, of which two x-values are considered beeing the same root. + ## `mergetol` is the tolerance, of which two x-values are considered being the same root. ## `maxiter` can be used to limit the number of iterations for each root. ## Returns a (possibly empty) sorted sequence with the solutions. var deg=p.degree diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim index 657782889..a30fad92a 100644 --- a/lib/pure/securehash.nim +++ b/lib/pure/securehash.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2015 Nim Contributers +# (c) Copyright 2015 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index bb6175a12..c606b4680 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -189,7 +189,7 @@ proc readBool*(s: Stream): bool = read(s, result) proc peekBool*(s: Stream): bool = - ## peeks a bool from the stream `s`. Raises `EIO` if an error occured. + ## peeks a bool from the stream `s`. Raises `EIO` if an error occurred. peek(s, result) proc readInt8*(s: Stream): int8 = diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e39756696..29ae52d47 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1245,7 +1245,7 @@ proc getDayOfWeek*(day, month, year: int): WeekDay = result = (d-1).WeekDay proc getDayOfWeekJulian*(day, month, year: int): WeekDay = - ## Returns the day of the week enum from day, month and year, according to the Julian calender. + ## Returns the day of the week enum from day, month and year, according to the Julian calendar. # Day & month start from one. let a = (14 - month) div 12 diff --git a/lib/system.nim b/lib/system.nim index 3ef9c1b31..904791a46 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -279,11 +279,6 @@ when not defined(niminheritable): when not defined(nimunion): {.pragma: unchecked.} -when defined(nimNewShared): - type - `shared`* {.magic: "Shared".} - guarded* {.magic: "Guarded".} - # comparison operators: proc `==` *[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} ## Checks whether values within the *same enum* have the same underlying value diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 1bbd89fe7..1abd8466d 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -103,9 +103,9 @@ else: proc c_setjmp(jmpb: C_JmpBuf): cint {. header: "<setjmp.h>", importc: "setjmp".} -proc c_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {. +proc c_signal(sign: cint, handler: proc (a: cint) {.noconv.}) {. importc: "signal", header: "<signal.h>".} -proc c_raise(sig: cint) {.importc: "raise", header: "<signal.h>".} +proc c_raise(sign: cint) {.importc: "raise", header: "<signal.h>".} proc c_fputs(c: cstring, f: C_TextFileStar) {.importc: "fputs", header: "<stdio.h>".} diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 8d1e04b8d..948f87410 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -316,7 +316,7 @@ when defined(endb): dbgAborting: bool # whether the debugger wants to abort when not defined(noSignalHandler): - proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = + proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} = template processSignal(s, action: expr) {.immediate, dirty.} = if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") elif s == SIGSEGV: @@ -342,13 +342,13 @@ when not defined(noSignalHandler): GC_disable() var buf = newStringOfCap(2000) rawWriteStackTrace(buf) - processSignal(sig, buf.add) # nice hu? currying a la Nim :-) + processSignal(sign, buf.add) # nice hu? currying a la Nim :-) showErrorMessage(buf) GC_enable() else: var msg: cstring template asgn(y: expr) = msg = y - processSignal(sig, asgn) + processSignal(sign, asgn) showErrorMessage(msg) when defined(endb): dbgAborting = true quit(1) # always quit when SIGABRT @@ -367,6 +367,6 @@ when not defined(noSignalHandler): proc setControlCHook(hook: proc () {.noconv.} not nil) = # ugly cast, but should work on all architectures: - type SignalHandler = proc (sig: cint) {.noconv, benign.} + type SignalHandler = proc (sign: cint) {.noconv, benign.} {.deprecated: [TSignalHandler: SignalHandler].} c_signal(SIGINT, cast[SignalHandler](hook)) diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 013dc55f8..a4676d26e 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -91,7 +91,7 @@ when allowForeignThreadGc: ## this thread will only be initialized once per thread, no matter how often ## it is called. ## - ## This function is availble only when ``--threads:on`` and ``--tlsEmulation:off`` + ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off`` ## switches are used if not localGcInitialized: localGcInitialized = true @@ -100,7 +100,7 @@ when allowForeignThreadGc: initGC() else: template setupForeignThreadGc*(): stmt = - {.error: "setupForeignThreadGc is availble only when ``--threads:on`` and ``--tlsEmulation:off`` are used".} + {.error: "setupForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".} # ----------------- stack management -------------------------------------- # inspired from Smart Eiffel diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 772d25343..d587d772f 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -74,7 +74,7 @@ proc getEnv*(key: string): string {.tags: [ReadIOEffect].} = builtin proc existsEnv*(key: string): bool {.tags: [ReadIOEffect].} = - ## Checks for the existance of an environment variable named `key`. + ## Checks for the existence of an environment variable named `key`. builtin proc fileExists*(filename: string): bool {.tags: [ReadIOEffect].} = @@ -189,7 +189,7 @@ proc get*(key: string): string = builtin proc exists*(key: string): bool = - ## Checks for the existance of a configuration 'key' + ## Checks for the existence of a configuration 'key' ## like 'gcc.options.always'. builtin diff --git a/tests/casestmt/tcaseexpr1.nim b/tests/casestmt/tcaseexpr1.nim index e5e08e25e..56acbbc8a 100644 --- a/tests/casestmt/tcaseexpr1.nim +++ b/tests/casestmt/tcaseexpr1.nim @@ -11,7 +11,7 @@ discard """ type E = enum A, B, C -proc foo(x): auto = +proc foo(x: int): auto = return case x of 1..9: "digit" else: "number" diff --git a/tests/generics/tbadgenericlambda.nim b/tests/generics/tbadgenericlambda.nim index 2ab8e724d..9fac150c1 100644 --- a/tests/generics/tbadgenericlambda.nim +++ b/tests/generics/tbadgenericlambda.nim @@ -3,5 +3,5 @@ discard """ line: 6 """ -let x = proc (x, y): auto = x + y +let x = proc (x, y: auto): auto = x + y diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim index 45450ccca..a045b32f8 100644 --- a/tests/generics/tgeneric0.nim +++ b/tests/generics/tgeneric0.nim @@ -18,7 +18,7 @@ proc foo[T](p: TType[T, range[0..2]]) = #bug #1366 -proc reversed(x) = +proc reversed(x: auto) = for i in countdown(x.low, x.high): echo i diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim index eb6ada3e5..41ee74557 100644 --- a/tests/generics/tgenericlambda.nim +++ b/tests/generics/tgenericlambda.nim @@ -5,15 +5,15 @@ discard """ proc test(x: proc (a, b: int): int) = echo x(5, 5) -test(proc (a, b): auto = a + b) +test(proc (a, b: auto): auto = a + b) -test do (a, b) -> auto: a + b +test do (a, b: auto) -> auto: a + b proc foreach[T](s: seq[T], body: proc(x: T)) = for e in s: body(e) -foreach(@[1,2,3]) do (x): +foreach(@[1,2,3]) do (x: auto): echo x proc foo = diff --git a/tests/macros/tclosuremacro.nim b/tests/macros/tclosuremacro.nim index cf51949ed..c29fbe1c8 100644 --- a/tests/macros/tclosuremacro.nim +++ b/tests/macros/tclosuremacro.nim @@ -26,7 +26,7 @@ proc noReturn(x: () -> void) = proc doWithOneAndTwo(f: (int, int) -> int): int = f(1,2) -echo twoParams(proc (a, b): auto = a + b) +echo twoParams(proc (a, b: auto): auto = a + b) echo twoParams((x, y) => x + y) echo oneParam(x => x+5) diff --git a/tests/misc/tupcomingfeatures.nim b/tests/misc/tupcomingfeatures.nim new file mode 100644 index 000000000..cf07b06df --- /dev/null +++ b/tests/misc/tupcomingfeatures.nim @@ -0,0 +1,39 @@ +discard """ + output: '''0 -2 0 +0 -2''' +""" + +{.this: self.} + +type + Foo {.partial.} = object + a, b: int + +type + tupcomingfeatures.Foo = object + x: int + +proc yay(self: Foo) = + echo a, " ", b, " ", x + +proc footest[T](self: var Foo, a: T) = + b = 1+a + yay() + +proc nongeneric(self: Foo) = + echo a, " ", b + +var ff: Foo +footest(ff, -3) +ff.nongeneric + +{.experimental.} +using + c: Foo + x, y: int + +proc usesSig(c) = + echo "yummy" + +proc foobar(c, y) = + echo "yay" diff --git a/tests/stdlib/nre/init.nim b/tests/stdlib/nre/init.nim index 1a1470842..26e668104 100644 --- a/tests/stdlib/nre/init.nim +++ b/tests/stdlib/nre/init.nim @@ -2,7 +2,7 @@ import unittest include nre suite "Test NRE initialization": - test "correct intialization": + test "correct initialization": check(re("[0-9]+") != nil) check(re("(?i)[0-9]+") != nil) diff --git a/tests/template/tit.nim b/tests/template/tit.nim index 9866711de..cf50d2f6f 100644 --- a/tests/template/tit.nim +++ b/tests/template/tit.nim @@ -5,7 +5,7 @@ template someIt(a, pred: expr): expr = var it {.inject.} = 0 pred -proc aProc(n) = +proc aProc(n: auto) = n.someIt(echo(it)) aProc(89) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index ff83379b8..150c55edc 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -223,7 +223,7 @@ proc jsTests(r: var TResults, cat: Category, options: string) = "varres/tvartup", "misc/tints", "misc/tunsignedinc"]: test "tests/" & testfile & ".nim" - for testfile in ["pure/strutils"]: + for testfile in ["pure/strutils", "pure/json"]: test "lib/" & testfile & ".nim" # ------------------------- manyloc ------------------------------------------- diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim index 26e4ae85e..442fbab92 100644 --- a/tests/tuples/tuple_with_nil.nim +++ b/tests/tuples/tuple_with_nil.nim @@ -345,7 +345,7 @@ proc writeformat(o: var Writer; p: pointer; fmt: Format) = ## Write pointer `i` according to format `fmt` using output object ## `o` and output function `add`. ## - ## Pointers are casted to unsigned int and formated as hexadecimal + ## Pointers are casted to unsigned int and formatted as hexadecimal ## with prefix unless specified otherwise. var f = fmt if f.typ == 0.char: @@ -584,7 +584,7 @@ proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} = ## Each part is either a literal string or a format specification. A ## format specification is a substring of the form ## "{[arg][:format]}" where `arg` is either empty or a number - ## refering to the arg-th argument and an additional field or array + ## referring to the arg-th argument and an additional field or array ## index. The format string is a string accepted by `parse`. let subpeg = sequence(capture(digits()), capture(?sequence(charSet({'.'}), *pegs.identStartChars(), *identChars())), diff --git a/tests/typerel/trettypeinference.nim b/tests/typerel/trettypeinference.nim index 41b4aa5ef..fa4e89cc8 100644 --- a/tests/typerel/trettypeinference.nim +++ b/tests/typerel/trettypeinference.nim @@ -5,8 +5,8 @@ discard """ import typetraits -proc plus(a, b): auto = a + b -proc makePair(a, b): auto = (first: a, second: b) +proc plus(a, b: auto): auto = a + b +proc makePair(a, b: auto): auto = (first: a, second: b) proc `+`(a, b: string): seq[string] = @[a, b] @@ -19,7 +19,7 @@ static: assert p[0].type is string echo i.type.name echo s.type.name -proc inst(a): auto = +proc inst(a: auto): auto = static: echo "instantiated for ", a.type.name result = a diff --git a/todo.txt b/todo.txt index 703139c9c..db3604283 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,9 @@ nim c --gc:v2 -r -d:useSysAssert -d:useGcAssert -d:smokeCycles -d:useRealtimeGc tests/gc/gcbench +- document ``this`` pragma +- document and stress test ``.partial`` object declarations + essential for 1.0 ================= diff --git a/web/news.txt b/web/news.txt index f27398ee1..46a1c5162 100644 --- a/web/news.txt +++ b/web/news.txt @@ -16,20 +16,34 @@ Changes affecting backwards compatibility ``table.mpairs`` iterator only the returned values can be modified, no longer the keys. - The deprecated Nim shebang notation ``#!`` was removed from the language. Use ``#?`` instead. +- The ``using`` statement now means something completely different. You can use the + new experimental ``this`` pragma to achieve a similar effect to what the old ``using`` statement tried to achieve. +- Typeless parameters have been removed from the language since it would + clash with ``using``. Library Additions ----------------- - The rlocks module has been added providing reentrant lock synchronization - primitive + primitive. Compiler Additions ------------------ - Added a new ``--noCppExceptions`` switch that allows to use default exception handling (no ``throw`` or ``try``/``catch`` generated) when compiling to C++ - code + code. + +Language Additions +------------------ + +- Nim now supports a ``.this`` pragma for more notational convenience. +- Nim now supports a different ``using`` statement for more convenience. +- Nim now supports ``partial`` object declarations to mitigate the problems + that arise when types are mutually dependent and yet should be kept in + different modules. + 2016-01-27 Nim in Action is now available! ========================================== |