From 02bbfa116403adeaf29c5446a971228053e11d0d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 19 Apr 2017 08:33:19 +0200 Subject: [PATCH 1/8] precise stack scanning for Nim's GCs; work in progress --- compiler/ast.nim | 3 +++ compiler/ccgexprs.nim | 43 ++++++++++++++++++------------------------- compiler/ccgstmts.nim | 5 +++++ compiler/ccgtypes.nim | 8 ++++---- compiler/cgen.nim | 23 ++++++++++++++++------- compiler/cgendata.nim | 2 +- compiler/options.nim | 3 ++- lib/system/excpt.nim | 21 +++++++++++++++++++-- 8 files changed, 68 insertions(+), 40 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 49ca1c5e03..a064ac72b6 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -739,6 +739,8 @@ type OnUnknown, # location is unknown (stack, heap or static) OnStatic, # in a static section OnStack, # location is on hardware stack + OnStackShadowDup, # location is on the stack but also replicated + # on the shadow stack OnHeap # location is on heap or global # (reference counting needed) TLocFlags* = set[TLocFlag] @@ -748,6 +750,7 @@ type flags*: TLocFlags # location's flags t*: PType # type of location r*: Rope # rope value of location (code generators) + dup*: Rope # duplicated location for precise stack scans # ---------------- end of backend information ------------------------------ diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b03f7c5aa1..71845ddfc4 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -132,32 +132,12 @@ proc genSetNode(p: BProc, n: PNode): Rope = else: result = genRawSetData(cs, size) -proc getStorageLoc(n: PNode): TStorageLoc = - case n.kind - of nkSym: - case n.sym.kind - of skParam, skTemp: - result = OnStack - of skVar, skForVar, skResult, skLet: - if sfGlobal in n.sym.flags: result = OnHeap - else: result = OnStack - of skConst: - if sfGlobal in n.sym.flags: result = OnHeap - else: result = OnUnknown - else: result = OnUnknown - of nkDerefExpr, nkHiddenDeref: - case n.sons[0].typ.kind - of tyVar: result = OnUnknown - of tyPtr: result = OnStack - of tyRef: result = OnHeap - else: internalError(n.info, "getStorageLoc") - of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv: - result = getStorageLoc(n.sons[0]) - else: result = OnUnknown - proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if dest.s == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) + elif dest.s == OnStackShadowDup: + linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) + linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src)) elif dest.s == OnHeap: # location is on heap # now the writer barrier is inlined for performance: @@ -184,6 +164,8 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(dest), rdLoc(src)) + if preciseStack(): + linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src)) proc asgnComplexity(n: PNode): int = if n != nil: @@ -241,7 +223,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # Consider: - # type TMyFastString {.shallow.} = string + # type MyFastString {.shallow.} = string # Due to the implementation of pragmas this would end up to set the # tfShallow flag for the built-in string type too! So we check only # here for this flag, where it is reasonably safe to do so @@ -259,6 +241,9 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) + if dest.s == OnStackShadowDup: + linefmt(p, cpsStmts, "#genericAssignDup((void*)&$1, (void*)$2, $3);$n", + dupLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # This function replaces all other methods for generating @@ -277,12 +262,17 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) + if dest.s == OnStackShadowDup: + linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc) of tyString: if needToCopy notin flags and src.s != OnStatic: genRefAssign(p, dest, src, flags) else: if dest.s == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) + elif dest.s == OnStackShadowDup: + linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) + linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc) elif dest.s == OnHeap: # we use a temporary to care for the dreaded self assignment: var tmp: TLoc @@ -293,6 +283,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", addrLoc(dest), rdLoc(src)) + if preciseStack(): + linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc) of tyProc: if needsComplexAssignment(dest.t): # optimize closure assignment: @@ -1170,6 +1162,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = var t = e.typ.skipTypes(abstractInst) getTemp(p, t, tmp) let isRef = t.kind == tyRef + let stck = stackPlacement(t) var r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, nil) @@ -1193,7 +1186,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = add(tmp2.r, field.loc.r) tmp2.k = locTemp tmp2.t = field.loc.t - tmp2.s = if isRef: OnHeap else: OnStack + tmp2.s = if isRef: OnHeap else: stck expr(p, it.sons[1], tmp2) if d.k == locNone: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index cc925b150c..80500f5081 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -803,6 +803,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) if optStackTrace in p.options: linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") + if p.gcFrameLen > 0: + linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n") inc p.inExceptBlock var i = 1 var catchAllPresent = false @@ -911,6 +913,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popSafePoint();$n") if optStackTrace in p.options: linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") + if p.gcFrameLen > 0: + linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n") + inc p.inExceptBlock var i = 1 while (i < length) and (t.sons[i].kind == nkExceptBranch): diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 2b7cfaea1c..0a4a85c6f9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -282,7 +282,7 @@ proc ccgIntroducedPtr(s: PSym): bool = proc fillResult(param: PSym) = fillLoc(param.loc, locParam, param.typ, ~"Result", - OnStack) + stackPlacement(param.typ)) if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ): incl(param.loc.flags, lfIndirect) param.loc.s = OnUnknown @@ -376,9 +376,9 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeDescAux(m, t, check) proc paramStorageLoc(param: PSym): TStorageLoc = - if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin { - tyArray, tyOpenArray, tyVarargs}: - result = OnStack + let t = param.typ.skipTypes({tyVar, tyTypeDesc}) + if t.kind notin {tyArray, tyOpenArray, tyVarargs}: + result = stackPlacement(t) else: result = OnUnknown diff --git a/compiler/cgen.nim b/compiler/cgen.nim index fd15d07934..dc021d17e8 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -244,6 +244,10 @@ proc addrLoc(a: TLoc): Rope = if lfIndirect notin a.flags and mapType(a.t) != ctArray: result = "(&" & result & ")" +proc dupLoc(a: TLoc): Rope = + result = a.dup + assert result != nil + proc rdCharLoc(a: TLoc): Rope = # read a location that may need a char-cast: result = rdLoc(a) @@ -288,12 +292,13 @@ proc resetLoc(p: BProc, loc: var TLoc) = if not isComplexValueType(typ): if containsGcRef: var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.t, OnStack) + initLoc(nilLoc, locTemp, loc.t, stackPlacement(typ)) nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc, {afSrcIsNil}) else: linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: + XXX use stackPlacement here? if optNilCheck in p.options: linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) if loc.s != OnStack: @@ -343,17 +348,21 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) result.k = locTemp result.t = t - result.s = OnStack + result.s = stackPlacement(t) result.flags = {} constructLoc(p, result, not needsInit) proc initGCFrame(p: BProc): Rope = - if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType] + if p.gcFrameLen > 0: + result = ropegc(p.module, """ + struct {#GcFrameBase b_; $1} GCF_;$n + GCF_.b_.L=$2;$n + #pushGcFrame((GcFrameBase*)&GCF_);$n""" % [ + p.gcFrameType, rope(p.gcFrameLen)]) proc deinitGCFrame(p: BProc): Rope = - if p.gcFrameId > 0: - result = ropecg(p.module, - "if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n") + if p.gcFrameLen > 0: + result = ropecg(p.module, "#popGcFrame();$n") proc localDebugInfo(p: BProc, s: PSym) = if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return @@ -370,7 +379,7 @@ proc localDebugInfo(p: BProc, s: PSym) = proc localVarDecl(p: BProc; s: PSym): Rope = if s.loc.k == locNone: - fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), OnStack) + fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), stackPlacement(s.typ)) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) result = getTypeDesc(p.module, s.typ) if s.constraint.isNil: diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index be087095f5..2f927c83e3 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -90,7 +90,7 @@ type splitDecls*: int # > 0 if we are in some context for C++ that # requires 'T x = T()' to become 'T x; x = T()' # (yes, C++ is weird like that) - gcFrameId*: Natural # for the GC stack marking + gcFrameLen*: int # the number of slots in the GC-Frame gcFrameType*: Rope # the struct {} we put the GC markers into sigConflicts*: CountTable[string] diff --git a/compiler/options.nim b/compiler/options.nim index c4a57f41c3..3e681c8d19 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -140,12 +140,13 @@ var gEvalExpr* = "" # expression for idetools --eval gLastCmdTime*: float # when caas is enabled, we measure each command gListFullPaths*: bool - isServing*: bool = false + gPreciseStack*: bool = false gNoNimblePath* = false gExperimentalMode*: bool proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc +template preciseStack*(): bool = gPreciseStack template compilationCachePresent*: untyped = {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 8ed1fbb38e..bdcb4c1f6a 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -38,20 +38,29 @@ proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.} proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.} proc chckNil(p: pointer) {.noinline, compilerproc, benign.} +type + GcFrame = ptr GcFrameHeader + GcFrameHeader {.compilerproc.} = object + len: int + prev: ptr GcFrameHeader + var framePtr {.threadvar.}: PFrame excHandler {.threadvar.}: PSafePoint # list of exception handlers # a global variable for the root of all try blocks currException {.threadvar.}: ref Exception + gcFramePtr {.threadvar.}: GcFrame type - FrameState = tuple[framePtr: PFrame, excHandler: PSafePoint, currException: ref Exception] + FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame, + excHandler: PSafePoint, currException: ref Exception] proc getFrameState*(): FrameState {.compilerRtl, inl.} = - return (framePtr, excHandler, currException) + return (gcFramePtr, framePtr, excHandler, currException) proc setFrameState*(state: FrameState) {.compilerRtl, inl.} = + gcFramePtr = state.gcFramePtr framePtr = state.framePtr excHandler = state.excHandler currException = state.currException @@ -64,6 +73,14 @@ proc popFrame {.compilerRtl, inl.} = proc setFrame*(s: PFrame) {.compilerRtl, inl.} = framePtr = s +proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr +proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev +proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s +proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} = + s.prev = gcFramePtr + zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer)) + gcFramePtr = s + proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = s.hasRaiseAction = false s.prev = excHandler From afa80092d378a6dbc116c0aa3ed3964fd8c599d6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 21 Apr 2017 09:40:27 +0200 Subject: [PATCH 2/8] wip --- compiler/ccgexprs.nim | 18 ++++++++++++++++++ compiler/cgen.nim | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 71845ddfc4..b8dd50086d 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -730,6 +730,9 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = else: internalError(e.info, "genTupleElem") addf(r, ".Field$1", [rope(i)]) putIntoDest(p, d, tupType.sons[i], r, a.s) + if a.s == OnStackShadowDup: + d.s = OnStackShadowDup + d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(tupType, i)) proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym = var ty = ty @@ -754,12 +757,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = # so we use Field$i addf(r, ".Field$1", [rope(f.position)]) putIntoDest(p, d, f.typ, r, a.s) + if a.s == OnStackShadowDup: + d.s = OnStackShadowDup + d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(ty, f.position)) else: let field = lookupFieldAgain(p, ty, f, r) if field.loc.r == nil: fillObjectFields(p.module, ty) if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty)) addf(r, ".$1", [field.loc.r]) putIntoDest(p, d, field.typ, r, a.s) + if a.s == OnStackShadowDup and field.loc.dup != nil: + d.s = OnStackShadowDup + d.dup = ropef("$1.$2", a.dup, field.loc.dup) #d.s = a.s proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) @@ -811,6 +820,9 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = genFieldCheck(p, e, r, field, ty) add(r, rfmt(nil, ".$1", field.loc.r)) putIntoDest(p, d, field.typ, r, a.s) + if a.s == OnStackShadowDup and field.loc.dup != nil: + d.s = OnStackShadowDup + d.dup = ropef("$1.$2", a.dup, field.loc.dup) else: genRecordField(p, e.sons[0], d) @@ -838,6 +850,9 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = d.inheritLocation(a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s) + if a.s == OnStackShadowDup: + d.s = OnStackShadowDup + d.dup = ropef("$1[($2)- $3]", a.dup, rdCharLoc(b), first) proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -858,6 +873,9 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) + if a.s == OnStackShadowDup: + d.s = OnStackShadowDup + d.dup = ropef("$1[$2]", a.dup, rdCharLoc(b)) proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc diff --git a/compiler/cgen.nim b/compiler/cgen.nim index dc021d17e8..bad346298c 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -298,7 +298,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = else: linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: - XXX use stackPlacement here? + # XXX use stackPlacement here? if optNilCheck in p.options: linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) if loc.s != OnStack: From 2d91c04f4eea1f0768b305b4903b4c455b9d06e8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 4 May 2017 16:03:22 +0200 Subject: [PATCH 3/8] added many tests concerning IP v6 parsing --- tests/stdlib/tparseipv6.nim | 217 ++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 tests/stdlib/tparseipv6.nim diff --git a/tests/stdlib/tparseipv6.nim b/tests/stdlib/tparseipv6.nim new file mode 100644 index 0000000000..3e1c23e584 --- /dev/null +++ b/tests/stdlib/tparseipv6.nim @@ -0,0 +1,217 @@ +discard """ + output: "all ok" +""" + +import net + +const + positives = [ + "::f:8:a8f:218.17.235.229", + "::b:228.19.241.2", + "::8:c:a:f:8.35.8.096", + "::3:e:a:bc:04.19.2.9", + "::2:212.242.248.19", + "::df:a5f:3.250.208.9", + "::8:c:5:e63:250.208.249.0", + "::b:f:181.12.9.98", + "::a:f8:77.08.243.232", + "::a:b:85:e4d9:252.9.229.056", + "941:c:8a:c:e::917", + "e8:7a:e:ad:88a:8:203.235.225.46", + "139c:9e::f8:254.08.21.249", + "b38:f0:e::f9:89.6.12.18", + "ef::8", + "5::ab", + "a::8:255.247.96.253", + "b:c0::c:254.248.95.254", + "::8c:2:99.251.024.3", + "98::c:247.240.249.57", + "9::9", + "628::f1ed:f", + "c::cca8", + "2::3:c", + "fde::8fcc:92:e", + "f::3", + "e85::7", + "8::b:f6", + "0::6:8ca", + "c8::6e:be8", + "87::e", + "6:9::a7:9", + "c::5", + "49::1:62", + "df:c0::f:9", + "a09a:8::21:887a", + "2:f::c", + "8bf5:5::2a6e:f8f", + "a:9e::bc:a", + "f:60::c:fd", + "59::52f:0:fa7", + "8268:6cf::f:9", + "c:abb::f", + "a:ff8d::9:7", + "05:c87::9c:9a", + "e:f::c:9a:1", + "ff6:8::962:e", + "9::bd", + "68:ec::6", + "3b8:f::94:3e9:9952", + "49b4:ae::899:b4", + "cb9:8e8:af::f4", + "8::10:9ae6:f9", + "b9::2:57", + "ff:fba9::d", + "4::a:8", + "caa:c:85a::2:3", + "5::a5:9", + "c:ad::a", + "9a:f:f65::b", + "f:df::9:0", + "c:b9::8de", + "d:f::a", + "ab88:d4:0::fc:8d", + "8f:ee2::3", + "f:f8::bf2:8c8", + "8::efc", + "e:5a::b", + "c:48::94", + "a:b:5::8", + "f:88f::f0a6", + "9:f:e::3", + "b::fedd", + "7b:f::c", + "edf4:7d::88", + "89::d", + "c0:a:62::ac", + "7:f::b", + "8::a2", + "0f::1", + "::", + "b:8::", + "44:a::", + "ef8f::", + "b:4:d::", + "a::", + "5a:8::", + "ddaf:ecbf::", + "f:bb:a1::", + "f8:f::", + "::e:38:ab:f8", + "::cd:c", + "::aa3:eb", + "::bf:9f9", + "::7ef:bf8a", + "::9", + "::a:9af", + "::315", + "::a:a", + "::aed3:a", + "f0eb:0:e8:b:c:a:254.098.233.17", + "bfa:7fc:c66d:15:e9a:ded:254.119.9.9", + "d:ffa8:9:a:879:3:202.39.08.245", + "8e:2:8:fa8a:f1d1:1aa8:252.254.245.81", + "5:d4:a:e9:8:8:06.38.98.253", + "9c5:4:a5c:f:a6:8c9d:05.250.8.2", + "d19a:2:f808:be:f:c:98.86.197.249", + "8:26ac:8:8:cb:f:242.00.254.85", + "38:e:1:0b88:f:0:8.89.248.92", + "e7:ff96:a:f:f:b:253.91.052.195", + "d:8:2:5:894:5:254.0.240.199", + "2:98:9:8aa:9c8f:fa:252.98.248.17", + "e9:d4f:890:ccbe:5:8:088.200.228.216", + "3:3:9:5:6a:df5:255.251.8.12", + "0280:3:8:8:4:9:255.000.251.249", + "8:af7:db:aa:0:9:238.248.250.255", + "ff:ee:9a:9252:a:289:059.083.18.255", + "9f6:5:fc9:b:a89:a:142.1.250.254", + "e:981a:da:bf94:9:f8:254.242.18.95", + "3c:1:4:f2:89:f:8.91.255.14", + "e::9a2:c:9.050.80.8", + "9::4a:07:fb:211.241.254.228", + "9be::2:e:215.189.48.188", + "f::f:d:069.148.99.168", + "f::a:97.18.240.47", + "c::a98e:1:251.253.252.254", + "668::82:214.87.208.9", + "9c0::cf0:ecb:253.208.238.255", + "a::0:f1:210.240.238.049", + "8::a:1:251.238.34.09", + "81:dfe::b8:8.255.249.248", + "d3::7:b:9:83.189.08.244", + "8::9:8:8:00.7.11.252", + "2:8::c:a8:250.221.9.249", + "2::f:99.8.249.247", + "c:22f5::5:2c:243.15.79.89", + "e:8e::da:251.243.255.2", + "f15f:9::a:255.70.247.218", + "f:b::9f38:31.220.94.022", + "9::9a48:03.98.249.119", + "d:d:9b87::2d:a:249.253.38.8", + "d86d:99b::a9b:5:242.236.8.244", + "eb:3::f:9cf:1.253.1.228", + "b::ba2:255.247.114.64", + "2f:ec:bcb::9:219.254.250.094", + "da8a:f6::a:e0:19.251.241.251", + "5e:c1::a:021.250.8.254", + "c:9::8c9b:248.219.212.252", + "2:a::8d4a:216.255.198.223", + "1f::66:255.30.08.150", + "bc2b:8f::2ff9:6.245.99.230", + "a:8::a8:9.251.246.255", + "f:7:7::98:06.14.1.208", + "e:2::9:218.249.255.254", + "79:f::6:250.255.98.246", + "47:9:fb9f::9:038.136.17.251", + "ed::a:247.9.23.239", + "6f::f1:88.254.119.9", + "a::d:218.199.236.0", + "fc88::9:203.196.04.95", + "::8.048.255.85", + "::253.07.255.36", + "9:d::253.7.178.229", + "::250.84.158.253", + "::8.55.204.248", + "2d:c::253.18.18.252", + "df9:88ca::248.255.108.17", + "8e9b::250.206.0.82", + "::209.8.254.209", + "::247.088.8.8", + "::cb:f:ba41:250.208.19.249", + "::fe:0e8:243.240.229.5", + "::c:223.251.5.226", + "::8:08.03.8.250", + "::f:8.88.11.255", + "::fda:48:aa:05.189.07.2", + "::8:c3f:f:240.06.212.255", + "::f:0aa:244.123.99.16", + "::c9b5:c:034.8.090.196", + "::98:c9:254.14.241.81" + ] + negatives = ["foo.bar", + "::::::::::::", + "yet another failure", + "de:6:c:ab5:6a::9:252.06.06.249", + "f9:5f7:fa38:9:b::b6:09.255.248.252", + "97:c:5b:81:8a::f5dd:144.252.250.9", + "9:8:cd:8:a9::f:247.255.09.255", + "18:1:8c:2:3::9:8.254.252.139", + "e:c298:3:e:a::bb12:254.246.05.250", + "e:e:c:8e:fd::8:253.8.49.231", + "9:97f:f:e929:8a::c9:0.8.252.10", + "0df:b24:7:89:c::2b:16.249.240.092", + "b:8f5f:485:c:9a::84c:178.7.249.34", + ] + +proc ok(pos: openarray[string]) = + for p in pos: + if not isIpAddress(p): + echo "failure ", p + +proc notok(neg: openarray[string]) = + for n in neg: + if isIpAddress(n): + echo "failure ", n + +ok(positives) +notok(negatives) +echo "all ok" From 0c271f54208c7ba0bac6ad2da87f60e7c6d8e37c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 13 Jul 2017 04:43:53 +0200 Subject: [PATCH 4/8] fixes #5871 --- lib/system.nim | 1 + lib/system/excpt.nim | 5 ++--- tests/exception/tfinally3.nim | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 424d9694de..8f653c1e01 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -442,6 +442,7 @@ type ## providing an exception message ## is bad style. trace: string + up: ref Exception # used for stacking exceptions. Not exported! SystemError* = object of Exception ## \ ## Abstract class for exceptions that the runtime system raises. diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index b0769b2695..e35b0bd5d2 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -73,12 +73,11 @@ proc popSafePoint {.compilerRtl, inl.} = excHandler = excHandler.prev proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = - #if e.parent.isNil: - # e.parent = currException + e.up = currException currException = e proc popCurrentException {.compilerRtl, inl.} = - currException = nil # currException.parent + currException = currException.up # some platforms have native support for stack traces: const diff --git a/tests/exception/tfinally3.nim b/tests/exception/tfinally3.nim index 8bccd1a7f8..037ca9553d 100644 --- a/tests/exception/tfinally3.nim +++ b/tests/exception/tfinally3.nim @@ -1,6 +1,11 @@ discard """ file: "tfinally3.nim" - output: "false" + output: '''false +Within finally->try +Traceback (most recent call last) +tfinally3.nim(24) tfinally3 +Error: unhandled exception: First [Exception]''' + exitCode: 1 """ # Test break in try statement: @@ -14,5 +19,11 @@ proc main: bool = echo main() #OUT false - - +# bug #5871 +try: + raise newException(Exception, "First") +finally: + try: + raise newException(Exception, "Within finally->try") + except: + echo getCurrentExceptionMsg() From bc738d63a728ee6030cc224c8808990a6f641feb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 13 Jul 2017 05:13:12 +0200 Subject: [PATCH 5/8] no interval arithmetic anymore to construct implicit range types; breaking change --- compiler/ccgexprs.nim | 61 ++++++++----------- compiler/ccgstmts.nim | 5 -- compiler/ccgtypes.nim | 8 +-- compiler/cgen.nim | 23 +++---- compiler/cgendata.nim | 2 +- compiler/semexprs.nim | 1 - compiler/semfold.nim | 130 ---------------------------------------- doc/manual/types.txt | 20 ------- lib/pure/md5.nim | 12 ++-- lib/pure/securehash.nim | 4 +- lib/pure/strutils.nim | 2 +- 11 files changed, 46 insertions(+), 222 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index e62956a0cc..0ec16710f6 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -134,12 +134,32 @@ proc genSetNode(p: BProc, n: PNode): Rope = else: result = genRawSetData(cs, size) +proc getStorageLoc(n: PNode): TStorageLoc = + case n.kind + of nkSym: + case n.sym.kind + of skParam, skTemp: + result = OnStack + of skVar, skForVar, skResult, skLet: + if sfGlobal in n.sym.flags: result = OnHeap + else: result = OnStack + of skConst: + if sfGlobal in n.sym.flags: result = OnHeap + else: result = OnUnknown + else: result = OnUnknown + of nkDerefExpr, nkHiddenDeref: + case n.sons[0].typ.kind + of tyVar: result = OnUnknown + of tyPtr: result = OnStack + of tyRef: result = OnHeap + else: internalError(n.info, "getStorageLoc") + of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv: + result = getStorageLoc(n.sons[0]) + else: result = OnUnknown + proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if dest.s == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) - elif dest.s == OnStackShadowDup: - linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) - linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src)) elif dest.s == OnHeap: # location is on heap # now the writer barrier is inlined for performance: @@ -166,8 +186,6 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(dest), rdLoc(src)) - if preciseStack(): - linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src)) proc asgnComplexity(n: PNode): int = if n != nil: @@ -225,7 +243,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # Consider: - # type MyFastString {.shallow.} = string + # type TMyFastString {.shallow.} = string # Due to the implementation of pragmas this would end up to set the # tfShallow flag for the built-in string type too! So we check only # here for this flag, where it is reasonably safe to do so @@ -243,9 +261,6 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) - if dest.s == OnStackShadowDup: - linefmt(p, cpsStmts, "#genericAssignDup((void*)&$1, (void*)$2, $3);$n", - dupLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # This function replaces all other methods for generating @@ -264,17 +279,12 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) - if dest.s == OnStackShadowDup: - linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc) of tyString: if needToCopy notin flags and src.s != OnStatic: genRefAssign(p, dest, src, flags) else: if dest.s == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) - elif dest.s == OnStackShadowDup: - linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) - linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc) elif dest.s == OnHeap: # we use a temporary to care for the dreaded self assignment: var tmp: TLoc @@ -285,8 +295,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", addrLoc(dest), rdLoc(src)) - if preciseStack(): - linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc) of tyProc: if needsComplexAssignment(dest.t): # optimize closure assignment: @@ -735,9 +743,6 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = else: internalError(e.info, "genTupleElem") addf(r, ".Field$1", [rope(i)]) putIntoDest(p, d, tupType.sons[i], r, a.s) - if a.s == OnStackShadowDup: - d.s = OnStackShadowDup - d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(tupType, i)) proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym = var ty = ty @@ -762,18 +767,12 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = # so we use Field$i addf(r, ".Field$1", [rope(f.position)]) putIntoDest(p, d, f.typ, r, a.s) - if a.s == OnStackShadowDup: - d.s = OnStackShadowDup - d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(ty, f.position)) else: let field = lookupFieldAgain(p, ty, f, r) if field.loc.r == nil: fillObjectFields(p.module, ty) if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty)) addf(r, ".$1", [field.loc.r]) putIntoDest(p, d, field.typ, r, a.s) - if a.s == OnStackShadowDup and field.loc.dup != nil: - d.s = OnStackShadowDup - d.dup = ropef("$1.$2", a.dup, field.loc.dup) #d.s = a.s proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) @@ -825,9 +824,6 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = genFieldCheck(p, e, r, field, ty) add(r, rfmt(nil, ".$1", field.loc.r)) putIntoDest(p, d, field.typ, r, a.s) - if a.s == OnStackShadowDup and field.loc.dup != nil: - d.s = OnStackShadowDup - d.dup = ropef("$1.$2", a.dup, field.loc.dup) else: genRecordField(p, e.sons[0], d) @@ -855,9 +851,6 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = d.inheritLocation(a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s) - if a.s == OnStackShadowDup: - d.s = OnStackShadowDup - d.dup = ropef("$1[($2)- $3]", a.dup, rdCharLoc(b), first) proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -878,9 +871,6 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) - if a.s == OnStackShadowDup: - d.s = OnStackShadowDup - d.dup = ropef("$1[$2]", a.dup, rdCharLoc(b)) proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -1185,7 +1175,6 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = var t = e.typ.skipTypes(abstractInst) getTemp(p, t, tmp) let isRef = t.kind == tyRef - let stck = stackPlacement(t) var r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, nil) @@ -1209,7 +1198,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = add(tmp2.r, field.loc.r) tmp2.k = locTemp tmp2.t = field.loc.t - tmp2.s = if isRef: OnHeap else: stck + tmp2.s = if isRef: OnHeap else: OnStack expr(p, it.sons[1], tmp2) if d.k == locNone: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 27bb89f608..378951d9db 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -802,8 +802,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) if optStackTrace in p.options: linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") - if p.gcFrameLen > 0: - linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n") inc p.inExceptBlock var i = 1 var catchAllPresent = false @@ -912,9 +910,6 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popSafePoint();$n") if optStackTrace in p.options: linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") - if p.gcFrameLen > 0: - linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n") - inc p.inExceptBlock var i = 1 while (i < length) and (t.sons[i].kind == nkExceptBranch): diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1c14d95f17..0c81ca8143 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -282,7 +282,7 @@ proc ccgIntroducedPtr(s: PSym): bool = proc fillResult(param: PSym) = fillLoc(param.loc, locParam, param.typ, ~"Result", - stackPlacement(param.typ)) + OnStack) if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ): incl(param.loc.flags, lfIndirect) param.loc.s = OnUnknown @@ -376,9 +376,9 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeDescAux(m, t, check) proc paramStorageLoc(param: PSym): TStorageLoc = - let t = param.typ.skipTypes({tyVar, tyTypeDesc}) - if t.kind notin {tyArray, tyOpenArray, tyVarargs}: - result = stackPlacement(t) + if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin { + tyArray, tyOpenArray, tyVarargs}: + result = OnStack else: result = OnUnknown diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d9e771ce77..3797a92c23 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -242,10 +242,6 @@ proc addrLoc(a: TLoc): Rope = if lfIndirect notin a.flags and mapType(a.t) != ctArray: result = "(&" & result & ")" -proc dupLoc(a: TLoc): Rope = - result = a.dup - assert result != nil - proc rdCharLoc(a: TLoc): Rope = # read a location that may need a char-cast: result = rdLoc(a) @@ -290,13 +286,12 @@ proc resetLoc(p: BProc, loc: var TLoc) = if not isComplexValueType(typ): if containsGcRef: var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.t, stackPlacement(typ)) + initLoc(nilLoc, locTemp, loc.t, OnStack) nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc, {afSrcIsNil}) else: linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: - # XXX use stackPlacement here? if optNilCheck in p.options: linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) if loc.s != OnStack: @@ -346,21 +341,17 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) result.k = locTemp result.t = t - result.s = stackPlacement(t) + result.s = OnStack result.flags = {} constructLoc(p, result, not needsInit) proc initGCFrame(p: BProc): Rope = - if p.gcFrameLen > 0: - result = ropegc(p.module, """ - struct {#GcFrameBase b_; $1} GCF_;$n - GCF_.b_.L=$2;$n - #pushGcFrame((GcFrameBase*)&GCF_);$n""" % [ - p.gcFrameType, rope(p.gcFrameLen)]) + if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType] proc deinitGCFrame(p: BProc): Rope = - if p.gcFrameLen > 0: - result = ropecg(p.module, "#popGcFrame();$n") + if p.gcFrameId > 0: + result = ropecg(p.module, + "if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n") proc localDebugInfo(p: BProc, s: PSym) = if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return @@ -377,7 +368,7 @@ proc localDebugInfo(p: BProc, s: PSym) = proc localVarDecl(p: BProc; s: PSym): Rope = if s.loc.k == locNone: - fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), stackPlacement(s.typ)) + fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), OnStack) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) result = getTypeDesc(p.module, s.typ) if s.constraint.isNil: diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 2f927c83e3..be087095f5 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -90,7 +90,7 @@ type splitDecls*: int # > 0 if we are in some context for C++ that # requires 'T x = T()' to become 'T x; x = T()' # (yes, C++ is weird like that) - gcFrameLen*: int # the number of slots in the GC-Frame + gcFrameId*: Natural # for the GC stack marking gcFrameType*: Rope # the struct {} we put the GC markers into sigConflicts*: CountTable[string] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 76d4be7662..5f48e2fc5a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -588,7 +588,6 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = result = semfold.getConstExpr(c.module, call) if result.isNil: result = n else: return result - result.typ = semfold.getIntervalType(callee.magic, call) block maybeLabelAsStatic: # XXX: temporary work-around needed for tlateboundstatic. diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 84cb0071f6..612df1ba32 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -92,26 +92,6 @@ proc pickIntRange(a, b: PType): PType = proc isIntRangeOrLit(t: PType): bool = result = isIntRange(t) or isIntLit(t) -proc pickMinInt(n: PNode): BiggestInt = - if n.kind in {nkIntLit..nkUInt64Lit}: - result = n.intVal - elif isIntLit(n.typ): - result = n.typ.n.intVal - elif isIntRange(n.typ): - result = firstOrd(n.typ) - else: - internalError(n.info, "pickMinInt") - -proc pickMaxInt(n: PNode): BiggestInt = - if n.kind in {nkIntLit..nkUInt64Lit}: - result = n.intVal - elif isIntLit(n.typ): - result = n.typ.n.intVal - elif isIntRange(n.typ): - result = lastOrd(n.typ) - else: - internalError(n.info, "pickMaxInt") - proc makeRange(typ: PType, first, last: BiggestInt): PType = let minA = min(first, last) let maxA = max(first, last) @@ -137,116 +117,6 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType = result.n = n addSonSkipIntLit(result, skipTypes(typ, {tyRange})) -proc getIntervalType*(m: TMagic, n: PNode): PType = - # Nim requires interval arithmetic for ``range`` types. Lots of tedious - # work but the feature is very nice for reducing explicit conversions. - const ordIntLit = {nkIntLit..nkUInt64Lit} - result = n.typ - - template commutativeOp(opr: untyped) = - let a = n.sons[1] - let b = n.sons[2] - if isIntRangeOrLit(a.typ) and isIntRangeOrLit(b.typ): - result = makeRange(pickIntRange(a.typ, b.typ), - opr(pickMinInt(a), pickMinInt(b)), - opr(pickMaxInt(a), pickMaxInt(b))) - - template binaryOp(opr: untyped) = - let a = n.sons[1] - let b = n.sons[2] - if isIntRange(a.typ) and b.kind in {nkIntLit..nkUInt64Lit}: - result = makeRange(a.typ, - opr(pickMinInt(a), pickMinInt(b)), - opr(pickMaxInt(a), pickMaxInt(b))) - - case m - of mUnaryMinusI, mUnaryMinusI64: - let a = n.sons[1].typ - if isIntRange(a): - # (1..3) * (-1) == (-3.. -1) - result = makeRange(a, 0|-|lastOrd(a), 0|-|firstOrd(a)) - of mUnaryMinusF64: - let a = n.sons[1].typ - if isFloatRange(a): - result = makeRangeF(a, -getFloat(a.n.sons[1]), - -getFloat(a.n.sons[0])) - of mAbsF64: - let a = n.sons[1].typ - if isFloatRange(a): - # abs(-5.. 1) == (1..5) - if a.n[0].floatVal <= 0.0: - result = makeRangeF(a, 0.0, abs(getFloat(a.n.sons[0]))) - else: - result = makeRangeF(a, abs(getFloat(a.n.sons[1])), - abs(getFloat(a.n.sons[0]))) - of mAbsI: - let a = n.sons[1].typ - if isIntRange(a): - if a.n[0].intVal <= 0: - result = makeRange(a, 0, `|abs|`(getInt(a.n.sons[0]))) - else: - result = makeRange(a, `|abs|`(getInt(a.n.sons[1])), - `|abs|`(getInt(a.n.sons[0]))) - of mSucc: - let a = n.sons[1].typ - let b = n.sons[2].typ - if isIntRange(a) and isIntLit(b): - # (-5.. 1) + 6 == (-5 + 6)..(-1 + 6) - result = makeRange(a, pickMinInt(n.sons[1]) |+| pickMinInt(n.sons[2]), - pickMaxInt(n.sons[1]) |+| pickMaxInt(n.sons[2])) - of mPred: - let a = n.sons[1].typ - let b = n.sons[2].typ - if isIntRange(a) and isIntLit(b): - result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]), - pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2])) - of mAddI, mAddU: - commutativeOp(`|+|`) - of mMulI, mMulU: - commutativeOp(`|*|`) - of mSubI, mSubU: - binaryOp(`|-|`) - of mBitandI: - # 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 ordIntLit: swap(a, b) - if b.kind in ordIntLit: - let x = b.intVal|+|1 - if (x and -x) == x and x >= 0: - result = makeRange(n.typ, 0, b.intVal) - of mModU: - let a = n.sons[1] - let b = n.sons[2] - if b.kind in ordIntLit: - if b.intVal >= 0: - result = makeRange(n.typ, 0, b.intVal-1) - else: - result = makeRange(n.typ, b.intVal+1, 0) - of mModI: - # so ... if you ever wondered about modulo's signedness; this defines it: - let a = n.sons[1] - let b = n.sons[2] - if b.kind in {nkIntLit..nkUInt64Lit}: - if b.intVal >= 0: - result = makeRange(n.typ, -(b.intVal-1), b.intVal-1) - else: - result = makeRange(n.typ, b.intVal+1, -(b.intVal+1)) - of mDivI, mDivU: - binaryOp(`|div|`) - of mMinI: - commutativeOp(min) - of mMaxI: - commutativeOp(max) - else: discard - -discard """ - mShlI, - mShrI, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 -""" - proc evalIs(n, a: PNode): PNode = # XXX: This should use the standard isOpImpl internalAssert a.kind == nkSym and a.sym.kind == skType diff --git a/doc/manual/types.txt b/doc/manual/types.txt index e6875f2df0..927cda9e1c 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -136,26 +136,6 @@ determined). Assignments from the base type to one of its subrange types A subrange type has the same size as its base type (``int`` in the example). -Nim requires `interval arithmetic`:idx: for subrange types over a set -of built-in operators that involve constants: ``x %% 3`` is of -type ``range[0..2]``. The following built-in operators for integers are -affected by this rule: ``-``, ``+``, ``*``, ``min``, ``max``, ``succ``, -``pred``, ``mod``, ``div``, ``%%``, ``and`` (bitwise ``and``). - -Bitwise ``and`` only produces a ``range`` if one of its operands is a -constant *x* so that (x+1) is a power of two. -(Bitwise ``and`` is then a ``%%`` operation.) - -This means that the following code is accepted: - -.. code-block:: nim - case (x and 3) + 7 - of 7: echo "A" - of 8: echo "B" - of 9: echo "C" - of 10: echo "D" - # note: no ``else`` required as (x and 3) + 7 has the type: range[7..10] - Pre-defined floating point types -------------------------------- diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 44b9ed0d4e..1ff3a9824c 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -78,10 +78,10 @@ proc encode(dest: var MD5Block, src: cstring) = proc decode(dest: var openArray[uint8], src: openArray[uint32]) = var i = 0 for j in 0..high(src): - dest[i] = src[j] and 0xff'u32 - dest[i+1] = src[j] shr 8 and 0xff'u32 - dest[i+2] = src[j] shr 16 and 0xff'u32 - dest[i+3] = src[j] shr 24 and 0xff'u32 + dest[i] = uint8(src[j] and 0xff'u32) + dest[i+1] = uint8(src[j] shr 8 and 0xff'u32) + dest[i+2] = uint8(src[j] shr 16 and 0xff'u32) + dest[i+3] = uint8(src[j] shr 24 and 0xff'u32) inc(i, 4) proc transform(buffer: pointer, state: var MD5State) = @@ -216,8 +216,8 @@ proc `$`*(d: MD5Digest): string = const digits = "0123456789abcdef" result = "" for i in 0..15: - add(result, digits[(d[i] shr 4) and 0xF]) - add(result, digits[d[i] and 0xF]) + add(result, digits[(d[i].int shr 4) and 0xF]) + add(result, digits[d[i].int and 0xF]) proc getMD5*(s: string): string = ## computes an MD5 value of `s` and returns its string representation diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim index f141732a70..c191466698 100644 --- a/lib/pure/securehash.nim +++ b/lib/pure/securehash.nim @@ -148,13 +148,13 @@ proc sha1(src: cstring; len: int): Sha1Digest = while lastBlockBytes < endCurrentBlock: var value = uint32(src[lastBlockBytes + currentBlock]) shl - ((3'u32 - (lastBlockBytes and 3)) shl 3) + ((3'u32 - uint32(lastBlockBytes and 3)) shl 3) w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value inc(lastBlockBytes) w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or ( - 0x80'u32 shl ((3'u32 - (lastBlockBytes and 3)) shl 3) + 0x80'u32 shl ((3'u32 - uint32(lastBlockBytes and 3)) shl 3) ) if endCurrentBlock >= 56: diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 20b2657f6f..346248a25d 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -887,7 +887,7 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect, n = x result = newString(len) for j in countdown(len-1, 0): - result[j] = HexChars[n and 0xF] + result[j] = HexChars[(n and 0xF).int] n = n shr 4 # handle negative overflow if n == 0 and x < 0: n = -1 From 8432eb6e67b4df011e50460342cf70a9939a0af8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 15 Sep 2017 09:56:30 +0200 Subject: [PATCH 6/8] make bootstrapping work under the new integer promotion rules --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3cfefb5c1c..e88002c7b9 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -669,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = t = x result = newString(len) for j in countdown(len-1, 0): - result[j] = HexChars[t and 0xF] + result[j] = HexChars[int(t and 0xF)] t = t shr 4 # handle negative overflow if t == 0 and x < 0: t = -1 From 9c513de2ae2346248d6fa99aa0b7943559f48ff7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 15 Sep 2017 09:57:12 +0200 Subject: [PATCH 7/8] update Nim's version --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 9efa850ed7..f967fb5f5d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1909,7 +1909,7 @@ const NimMinor*: int = 17 ## is the minor number of Nim's version. - NimPatch*: int = 1 + NimPatch*: int = 3 ## is the patch number of Nim's version. NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch From 50ae228e255c0f82a1620657521a27f6b8152b58 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 15 Sep 2017 10:30:58 +0200 Subject: [PATCH 8/8] closes #5854 --- tests/arithm/tsubrange.nim | 13 +++++++++++++ todo.txt | 1 + 2 files changed, 14 insertions(+) create mode 100644 tests/arithm/tsubrange.nim diff --git a/tests/arithm/tsubrange.nim b/tests/arithm/tsubrange.nim new file mode 100644 index 0000000000..9d60dbd1aa --- /dev/null +++ b/tests/arithm/tsubrange.nim @@ -0,0 +1,13 @@ +discard """ + output: '''1''' +""" + +# bug #5854 +type + n16* = range[0'i16..high(int16)] + +var level: n16 = 1 +let maxLevel: n16 = 1 + +level = min(level + 2, maxLevel) +echo level diff --git a/todo.txt b/todo.txt index 7597c436af..1678c1599d 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ version 1.0 battle plan ======================= +- eliminate 'nil' for seqs and strings - make 'not nil' the default (produce warnings instead of errors for a smooth migration path) - case objects needs to be safe and need to support pattern matching