From d8746398c43cae51d80b32ac182ab12a64710286 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 9 Oct 2017 21:11:53 +0200 Subject: allow macros to produce nnkGotoState and nkState --- compiler/semexprs.nim | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'compiler/semexprs.nim') diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 439f2772a..c336afc89 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2357,6 +2357,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): localError(n.info, errGenerated, "'defer' takes a 'void' expression") #localError(n.info, errGenerated, "'defer' not allowed in this context") + of nkGotoState, nkState: + if n.len != 1 and n.len != 2: illFormedAst(n) + for i in 0 ..< n.len: + n.sons[i] = semExpr(c, n.sons[i]) else: localError(n.info, errInvalidExpressionX, renderTree(n, {renderNoComments})) -- cgit 1.4.1-2-gfad0 From a2e4ab2e4e3570cd313e8d5422b276625f87e0d7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 12 Oct 2017 17:42:15 +0200 Subject: simple programs now work with the new destroyer pass --- compiler/destroyer.nim | 86 ++++++++++++++++++++++++++------------------------ compiler/semexprs.nim | 7 ++-- compiler/semstmts.nim | 21 ++++++------ compiler/transf.nim | 2 +- 4 files changed, 60 insertions(+), 56 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index db26c5366..a650df7dd 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -179,28 +179,34 @@ proc genDestroy(t: PType; dest: PNode): PNode = assert t.destructor != nil result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest)) +proc addTopVar(c: var Con; v: PNode) = + c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode) + +proc p(n: PNode; c: var Con): PNode + +template recurse(n, dest) = + for i in 0.. 0: c.addTopVar(newSymNode c.tmp) result = newNodeI(nkStmtList, n.info) if c.topLevelVars.len > 0: result.add c.topLevelVars if c.destroys.len > 0: - result.add newTryFinally(stmtList, c.destroys) + result.add newTryFinally(body, c.destroys) else: - result.add stmtList + result.add body + + echo "transformed into: ", result diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c336afc89..e8989f832 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1383,9 +1383,10 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = typeMismatch(n.info, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs, n.info) - if tfHasAsgn in lhs.typ.flags and not lhsIsResult and - mode != noOverloadedAsgn: - return overloadedAsgn(c, lhs, n.sons[1]) + when not newDestructors: + if tfHasAsgn in lhs.typ.flags and not lhsIsResult and + mode != noOverloadedAsgn: + return overloadedAsgn(c, lhs, n.sons[1]) fixAbstractType(c, n) asgnToResultVar(c, n, n.sons[0], n.sons[1]) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ccf332d3a..c8eda9d94 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -100,15 +100,16 @@ proc semProc(c: PContext, n: PNode): PNode include semdestruct proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = - if efAllowDestructor notin flags and - n.kind in nkCallKinds+{nkObjConstr,nkBracket}: - if instantiateDestructor(c, n.typ) != nil: - localError(n.info, warnDestructor) - # This still breaks too many things: - when false: - if efDetermineType notin flags and n.typ.kind == tyTypeDesc and - c.p.owner.kind notin {skTemplate, skMacro}: - localError(n.info, errGenerated, "value expected, but got a type") + when not newDestructors: + if efAllowDestructor notin flags and + n.kind in nkCallKinds+{nkObjConstr,nkBracket}: + if instantiateDestructor(c, n.typ) != nil: + localError(n.info, warnDestructor) + # This still breaks too many things: + when false: + if efDetermineType notin flags and n.typ.kind == tyTypeDesc and + c.p.owner.kind notin {skTemplate, skMacro}: + localError(n.info, errGenerated, "value expected, but got a type") proc semExprBranch(c: PContext, n: PNode): PNode = result = semExpr(c, n) @@ -116,7 +117,7 @@ proc semExprBranch(c: PContext, n: PNode): PNode = # XXX tyGenericInst here? semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) - when not newDestructors: semDestructorCheck(c, result, {}) + semDestructorCheck(c, result, {}) proc semExprBranchScope(c: PContext, n: PNode): PNode = openScope(c) diff --git a/compiler/transf.nim b/compiler/transf.nim index f5ea8feb9..2a33c60a6 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -973,7 +973,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = incl(result.flags, nfTransf) when useEffectSystem: trackProc(prc, result) if prc.kind == skFunc: - result = injectDestructorCalls(prc, n) + result = injectDestructorCalls(prc, result) #if prc.name.s == "testbody": # echo renderTree(result) -- cgit 1.4.1-2-gfad0 From 63530be0b8ca8a4727abe8bd7b8a371c4dd493a6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 13 Oct 2017 06:38:59 +0200 Subject: introduce --newruntime switch for the upcoming destructors and move semantics --- compiler/commands.nim | 4 ++++ compiler/destroyer.nim | 2 +- compiler/options.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semstmts.nim | 9 ++++----- compiler/transf.nim | 11 ++++++++--- 6 files changed, 19 insertions(+), 11 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/commands.nim b/compiler/commands.nim index ce50b1a79..bae1fda38 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -666,6 +666,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectArg(switch, arg, pass, info) if config != nil: config.cppDefine(arg) + of "newruntime": + expectNoArg(switch, arg, pass, info) + newDestructors = true + defineSymbol("nimNewRuntime") else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index a650df7dd..e8db4bc2b 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -290,4 +290,4 @@ proc injectDestructorCalls*(owner: PSym; n: PNode): PNode = else: result.add body - echo "transformed into: ", result + #echo "transformed into: ", result diff --git a/compiler/options.nim b/compiler/options.nim index 49a317582..86e6006d7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -14,7 +14,6 @@ const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true useWriteTracking* = false - newDestructors* = defined(nimV2) hasFFI* = defined(useFFI) newScopeForIf* = true useCaas* = not defined(noCaas) @@ -145,6 +144,7 @@ var isServing*: bool = false gNoNimblePath* = false gExperimentalMode*: bool + newDestructors*: bool proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e8989f832..2499c2ab6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1383,7 +1383,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = typeMismatch(n.info, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs, n.info) - when not newDestructors: + if not newDestructors: if tfHasAsgn in lhs.typ.flags and not lhsIsResult and mode != noOverloadedAsgn: return overloadedAsgn(c, lhs, n.sons[1]) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c8eda9d94..b65443b2f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -100,7 +100,7 @@ proc semProc(c: PContext, n: PNode): PNode include semdestruct proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = - when not newDestructors: + if not newDestructors: if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr,nkBracket}: if instantiateDestructor(c, n.typ) != nil: @@ -608,7 +608,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if def.kind == nkPar: v.ast = def[j] setVarType(v, tup.sons[j]) b.sons[j] = newSymNode(v) - when not newDestructors: addDefer(c, result, v) + if not newDestructors: addDefer(c, result, v) checkNilable(v) if sfCompileTime in v.flags: hasCompileTime = true if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result) @@ -1278,9 +1278,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = case s.name.s.normalize of "destroy", "=destroy": doDestructorStuff(c, s, n) - when not newDestructors: - if not experimentalMode(c): - localError n.info, "use the {.experimental.} pragma to enable destructors" + if not newDestructors and not experimentalMode(c): + localError n.info, "use the {.experimental.} pragma to enable destructors" incl(s.flags, sfUsed) of "deepcopy", "=deepcopy": if s.typ.len == 2 and diff --git a/compiler/transf.nim b/compiler/transf.nim index 2a33c60a6..fa468d493 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -43,7 +43,7 @@ type inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' - deferDetected, tooEarly: bool + deferDetected, tooEarly, needsDestroyPass: bool PTransf = ref TTransfContext proc newTransNode(a: PNode): PTransNode {.inline.} = @@ -780,7 +780,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = nkBlockStmt, nkBlockExpr}: oldDeferAnchor = c.deferAnchor c.deferAnchor = n - + if n.typ != nil and tfHasAsgn in n.typ.flags: + c.needsDestroyPass = true case n.kind of nkSym: result = transformSym(c, n) @@ -972,7 +973,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = #result = liftLambdas(prc, result) incl(result.flags, nfTransf) when useEffectSystem: trackProc(prc, result) - if prc.kind == skFunc: + if c.needsDestroyPass and newDestructors: result = injectDestructorCalls(prc, result) #if prc.name.s == "testbody": # echo renderTree(result) @@ -989,6 +990,8 @@ proc transformStmt*(module: PSym, n: PNode): PNode = when useEffectSystem: trackTopLevelStmt(module, result) #if n.info ?? "temp.nim": # echo renderTree(result, {renderIds}) + if c.needsDestroyPass and newDestructors: + result = injectDestructorCalls(module, result) proc transformExpr*(module: PSym, n: PNode): PNode = if nfTransf in n.flags: @@ -998,3 +1001,5 @@ proc transformExpr*(module: PSym, n: PNode): PNode = result = processTransf(c, n, module) liftDefer(c, result) incl(result.flags, nfTransf) + if c.needsDestroyPass and newDestructors: + result = injectDestructorCalls(module, result) -- cgit 1.4.1-2-gfad0 From a75f3b36619cf06aee324b80b908fbcdc58b43d8 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 14 Oct 2017 22:34:19 +0200 Subject: fixes #4910 --- compiler/semexprs.nim | 11 +++++++---- tests/cpp/tget_subsystem.nim | 8 ++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2499c2ab6..180754168 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1296,10 +1296,13 @@ proc takeImplicitAddr(c: PContext, n: PNode): PNode = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le.sons[0] - if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult: - n.sons[0] = x # 'result[]' --> 'result' - n.sons[1] = takeImplicitAddr(c, ri) - x.typ.flags.incl tfVarIsPtr + if x.typ.kind == tyVar and x.kind == nkSym: + if x.sym.kind == skResult: + n.sons[0] = x # 'result[]' --> 'result' + n.sons[1] = takeImplicitAddr(c, ri) + if x.sym.kind != skParam: + # XXX This is hacky. See bug #4910. + x.typ.flags.incl tfVarIsPtr #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info template resultTypeIsInferrable(typ: PType): untyped = diff --git a/tests/cpp/tget_subsystem.nim b/tests/cpp/tget_subsystem.nim index 461914739..81009dd39 100644 --- a/tests/cpp/tget_subsystem.nim +++ b/tests/cpp/tget_subsystem.nim @@ -21,3 +21,11 @@ proc getSubsystem*[T](): ptr T {. let input: ptr Input = getSubsystem[Input]() + +# bug #4910 + +proc foo() = + var ts: array[10, int] + for t in mitems(ts): + t = 123 + -- cgit 1.4.1-2-gfad0 From 45d74f408151f21f1593bfc04bd29ea7509ddae9 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 17 Oct 2017 12:46:49 +0200 Subject: destructors: preparations for upcoming changes --- compiler/ast.nim | 2 ++ compiler/destroyer.nim | 3 +-- compiler/semasgn.nim | 12 +++++++++++- compiler/semdata.nim | 1 + compiler/semexprs.nim | 3 +++ compiler/semstmts.nim | 1 + compiler/semtypinst.nim | 6 +----- lib/system.nim | 7 +++++++ 8 files changed, 27 insertions(+), 8 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/ast.nim b/compiler/ast.nim index 6519b698a..cdc78be74 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1663,3 +1663,5 @@ when false: if n.isNil: return true for i in 0 ..< n.safeLen: if n[i].containsNil: return true + +template hasDestructor*(t: PType): bool = tfHasAsgn in t.flags diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index afa2e5e50..f97c44861 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -95,8 +95,6 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, strutils, options, dfa, lowerings -template hasDestructor(t: PType): bool = tfHasAsgn in t.flags - const InterestingSyms = {skVar, skResult, skLet} @@ -253,6 +251,7 @@ proc p(n: PNode; c: var Con): PNode = result = copyNode(n) recurse(n, result) of nkAsgn, nkFastAsgn: + # XXX if special, call special operator if n[0].kind == nkSym and interestingSym(n[0].sym): result = moveOrCopy(n[0], n[1], c) else: diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index caed11341..27626a1d0 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -109,7 +109,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = styleCheckUse(c.info, op) body.add newOpCall(op, x) result = true - of attachedAsgn: + of attachedAsgn, attachedSink: if tfHasAsgn in t.flags: var op: PSym if sameType(t, c.asgnForType): @@ -285,3 +285,13 @@ proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = let a = getAsgnOrLiftBody(c, dest.typ, dest.info) result = newAsgnCall(c, a, dest, src) + +proc liftTypeBoundOps*(c: PContext; typ: PType) = + ## In the semantic pass this is called in strategic places + ## to ensure we lift assignment, destructors and moves properly. + ## Since this is done in the sem* routines generics already have + ## been resolved for us and do not complicate the logic any further. + ## We have to ensure that the 'tfHasDesctructor' flags bubbles up + ## in the generic instantiations though. + ## The later 'destroyer' pass depends on it. + if not newDestructors or not hasDestructor(typ): return diff --git a/compiler/semdata.nim b/compiler/semdata.nim index a3f0f715b..8b5803a7f 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -70,6 +70,7 @@ type TTypeAttachedOp* = enum attachedAsgn, + attachedSink, attachedDeepCopy, attachedDestructor diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 180754168..465b09814 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -670,6 +670,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) + if result.typ != nil: liftTypeBoundOps(c, result.typ) if c.matchedConcept == nil: result = evalAtCompileTime(c, result) @@ -1390,6 +1391,8 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if tfHasAsgn in lhs.typ.flags and not lhsIsResult and mode != noOverloadedAsgn: return overloadedAsgn(c, lhs, n.sons[1]) + else: + liftTypeBoundOps(c, lhs.typ) fixAbstractType(c, n) asgnToResultVar(c, n, n.sons[0], n.sons[1]) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c6e03cef3..3c26be89e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -553,6 +553,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # this can only happen for errornous var statements: if typ == nil: continue typeAllowedCheck(a.info, typ, symkind) + liftTypeBoundOps(c, typ) var tup = skipTypes(typ, {tyGenericInst, tyAlias}) if a.kind == nkVarTuple: if tup.kind != tyTuple: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 172a557b3..5d0f0177c 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -363,11 +363,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # '=' needs to be instantiated for generics when the type is constructed: newbody.field = cl.c.instTypeBoundOp(cl.c, opr, result, cl.info, attachedAsgn, 1) - # we need to produce the destructor first here because generated '=' - # and '=sink' operators can rely on it: - if newDestructors: typeBound(destructor) - typeBound(assignment) - typeBound(sink) + if not newDestructors: typeBound(assignment) let methods = skipTypes(bbody, abstractPtrs).methods for col, meth in items(methods): # we instantiate the known methods belonging to that type, this causes diff --git a/lib/system.nim b/lib/system.nim index d19a406cb..d85f272f2 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -292,6 +292,13 @@ when defined(nimArrIdx): proc `[]=`*[I: Ordinal;T,S](a: T; i: I; x: S) {.noSideEffect, magic: "ArrPut".} proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} + when defined(nimNewRuntime): + template `=destroy`*[T](x: var T) = + ## generic `destructor`:idx: implementation that can be overriden. + discard + template `=sink`*[T](x: var T; y: T) = + ## generic `sink`:idx: implementation that can be overriden. + shallowCopy(x, y) type Slice*[T] = object ## builtin slice type -- cgit 1.4.1-2-gfad0 From 58d5f9679028516b09bc2a2c0498e668f61cc8c9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 17 Oct 2017 14:48:16 +0200 Subject: destructors: lift type bound ops to objects and arrays etc --- compiler/semasgn.nim | 92 ++++++++++++++++++++++++++----------- compiler/semexprs.nim | 4 +- compiler/semstmts.nim | 2 +- tests/destructor/tcustomstrings.nim | 5 +- 4 files changed, 71 insertions(+), 32 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 27626a1d0..19d31ec73 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -22,7 +22,8 @@ type recurse: bool proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) -proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym +proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; + info: TLineInfo): PSym {.discardable.} proc at(a, i: PNode, elemType: PType): PNode = result = newNodeI(nkBracketExpr, a.info, 2) @@ -97,9 +98,37 @@ proc newOpCall(op: PSym; x: PNode): PNode = result.add(newSymNode(op)) result.add x +proc destructorCall(c: PContext; op: PSym; x: PNode): PNode = + result = newNodeIT(nkCall, x.info, op.typ.sons[0]) + result.add(newSymNode(op)) + if newDestructors: + result.add genAddr(c, x) + else: + result.add x + proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = result = newAsgnStmt(x, newOpCall(op, y)) +proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; + field: PSym): bool = + if tfHasAsgn in t.flags: + var op: PSym + if sameType(t, c.asgnForType): + # generate recursive call: + if c.recurse: + op = c.fn + else: + c.recurse = true + return false + else: + op = field + if op == nil: + op = liftBody(c.c, t, c.kind, c.info) + markUsed(c.info, op, c.c.graph.usageSym) + styleCheckUse(c.info, op) + body.add newAsgnCall(c.c, op, x, y) + result = true + proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = case c.kind of attachedDestructor: @@ -107,26 +136,12 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = if op != nil: markUsed(c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) - body.add newOpCall(op, x) - result = true - of attachedAsgn, attachedSink: - if tfHasAsgn in t.flags: - var op: PSym - if sameType(t, c.asgnForType): - # generate recursive call: - if c.recurse: - op = c.fn - else: - c.recurse = true - return false - else: - op = t.assignment - if op == nil: - op = liftBody(c.c, t, c.info) - markUsed(c.info, op, c.c.graph.usageSym) - styleCheckUse(c.info, op) - body.add newAsgnCall(c.c, op, x, y) + body.add destructorCall(c.c, op, x) result = true + of attachedAsgn: + result = considerAsgnOrSink(c, t, body, x, y, t.assignment) + of attachedSink: + result = considerAsgnOrSink(c, t, body, x, y, t.sink) of attachedDeepCopy: let op = t.deepCopy if op != nil: @@ -245,12 +260,20 @@ proc addParam(procType: PType; param: PSym) = addSon(procType.n, newSymNode(param)) rawAddSon(procType, param.typ) -proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym = +proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; + info: TLineInfo): PSym {.discardable.} = var a: TLiftCtx a.info = info a.c = c + a.kind = kind let body = newNodeI(nkStmtList, info) - result = newSym(skProc, getIdent":lifted=", typ.owner, info) + let procname = case kind + of attachedAsgn: getIdent":Asgn" + of attachedSink: getIdent":Sink" + of attachedDeepCopy: getIdent":DeepCopy" + of attachedDestructor: getIdent":Destroy" + + result = newSym(skProc, procname, typ.owner, info) a.fn = result a.asgnForType = typ @@ -261,7 +284,16 @@ proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym = result.typ = newProcType(info, typ.owner) result.typ.addParam dest - result.typ.addParam src + if kind != attachedDestructor: + result.typ.addParam src + + # recursion is handled explicitly, but register the type based operation + # here in order to keep things robust against runaway recursions: + case kind + of attachedAsgn: typ.assignment = result + of attachedSink: typ.sink = result + of attachedDeepCopy: typ.deepCopy = result + of attachedDestructor: typ.destructor = result liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) @@ -272,21 +304,18 @@ proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym = n.sons[bodyPos] = body result.ast = n - # register late as recursion is handled differently - typ.assignment = result - #echo "Produced this ", n proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = let t = typ.skipTypes({tyGenericInst, tyVar, tyAlias}) result = t.assignment if result.isNil: - result = liftBody(c, t, info) + result = liftBody(c, t, attachedAsgn, info) proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = let a = getAsgnOrLiftBody(c, dest.typ, dest.info) result = newAsgnCall(c, a, dest, src) -proc liftTypeBoundOps*(c: PContext; typ: PType) = +proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = ## In the semantic pass this is called in strategic places ## to ensure we lift assignment, destructors and moves properly. ## Since this is done in the sem* routines generics already have @@ -295,3 +324,10 @@ proc liftTypeBoundOps*(c: PContext; typ: PType) = ## in the generic instantiations though. ## The later 'destroyer' pass depends on it. if not newDestructors or not hasDestructor(typ): return + # we generate the destructor first so that other operators can depend on it: + if typ.destructor == nil: + liftBody(c, typ, attachedDestructor, info) + if typ.assignment == nil: + liftBody(c, typ, attachedAsgn, info) + if typ.sink == nil: + liftBody(c, typ, attachedSink, info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 465b09814..49dfbe8f4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -670,7 +670,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) - if result.typ != nil: liftTypeBoundOps(c, result.typ) + if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info) if c.matchedConcept == nil: result = evalAtCompileTime(c, result) @@ -1392,7 +1392,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = mode != noOverloadedAsgn: return overloadedAsgn(c, lhs, n.sons[1]) else: - liftTypeBoundOps(c, lhs.typ) + liftTypeBoundOps(c, lhs.typ, lhs.info) fixAbstractType(c, n) asgnToResultVar(c, n, n.sons[0], n.sons[1]) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3c26be89e..d6c2a9ea1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -553,7 +553,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # this can only happen for errornous var statements: if typ == nil: continue typeAllowedCheck(a.info, typ, symkind) - liftTypeBoundOps(c, typ) + liftTypeBoundOps(c, typ, a.info) var tup = skipTypes(typ, {tyGenericInst, tyAlias}) if a.kind == nkVarTuple: if tup.kind != tyTuple: diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim index 2250d4772..780b0d2f1 100644 --- a/tests/destructor/tcustomstrings.nim +++ b/tests/destructor/tcustomstrings.nim @@ -4,7 +4,7 @@ foo bar to appendmore here foo bar to appendmore here foo bar to appendmore here foo bar to appendmore here -after 16 16''' +after 20 20''' cmd: '''nim c --newruntime $file''' """ @@ -92,5 +92,8 @@ proc main(n: int) = a.add c echo cstring(a.data) + var x: array[4, mystring] + for i in 0..high(x): x[i] = create"added to array" + main(1000) echo "after ", allocCount, " ", deallocCount -- cgit 1.4.1-2-gfad0 From b407f083bafa227470caa077b2f70a6bc4da2dc3 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 24 Oct 2017 09:40:28 +0200 Subject: destructors: work in progress --- compiler/destroyer.nim | 26 +++++++++++- compiler/sem.nim | 20 +++++---- compiler/semasgn.nim | 46 ++++++++++++++++---- compiler/semexprs.nim | 13 ++++++ compiler/semmagic.nim | 11 +++-- compiler/transf.nim | 3 ++ lib/system.nim | 4 +- tests/destructor/tbintree.nim | 97 ------------------------------------------ tests/destructor/topttree.nim | 98 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 200 insertions(+), 118 deletions(-) delete mode 100644 tests/destructor/tbintree.nim create mode 100644 tests/destructor/topttree.nim (limited to 'compiler/semexprs.nim') diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index fde592f52..a868a64ba 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -93,7 +93,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - strutils, options, dfa, lowerings + strutils, options, dfa, lowerings, rodread const InterestingSyms = {skVar, skResult, skLet} @@ -164,20 +164,44 @@ proc isHarmlessVar*(s: PSym; c: Con): bool = template interestingSym(s: PSym): bool = s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) +proc patchHead(n: PNode; alreadyPatched: var IntSet) = + if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1: + let s = n[0].sym + if sfFromGeneric in s.flags or true: + if s.name.s[0] == '=' and not containsOrIncl(alreadyPatched, s.id): + patchHead(s.getBody, alreadyPatched) + let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) + template patch(op, field) = + if s.name.s == op and field != nil: #and field != s: + n.sons[0].sym = field + patch "=sink", t.sink + patch "=", t.assignment + patch "=destroy", t.destructor + for x in n: + patchHead(x, alreadyPatched) + +template patchHead(n: PNode) = + when false: + var alreadyPatched = initIntSet() + patchHead(n, alreadyPatched) + proc genSink(t: PType; dest: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias}) let op = if t.sink != nil: t.sink else: t.assignment assert op != nil + patchHead op.ast[bodyPos] result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest)) proc genCopy(t: PType; dest: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias}) assert t.assignment != nil + patchHead t.assignment.ast[bodyPos] result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest)) proc genDestroy(t: PType; dest: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias}) assert t.destructor != nil + patchHead t.destructor.ast[bodyPos] result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest)) proc addTopVar(c: var Con; v: PNode) = diff --git a/compiler/sem.nim b/compiler/sem.nim index ebfdafea7..5e98d7a65 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -522,14 +522,18 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = else: result = n result = semStmt(c, result) - # BUGFIX: process newly generated generics here, not at the end! - if c.lastGenericIdx < c.generics.len: - var a = newNodeI(nkStmtList, n.info) - addCodeForGenerics(c, a) - if sonsLen(a) > 0: - # a generic has been added to `a`: - if result.kind != nkEmpty: addSon(a, result) - result = a + when false: + # Code generators are lazy now and can deal with undeclared procs, so these + # steps are not required anymore and actually harmful for the upcoming + # destructor support. + # BUGFIX: process newly generated generics here, not at the end! + if c.lastGenericIdx < c.generics.len: + var a = newNodeI(nkStmtList, n.info) + addCodeForGenerics(c, a) + if sonsLen(a) > 0: + # a generic has been added to `a`: + if result.kind != nkEmpty: addSon(a, result) + result = a result = hloStmt(c, result) if gCmd == cmdInteractive and not isEmptyType(result.typ): result = buildEchoStmt(c, result) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 895946281..0454ea379 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -268,10 +268,10 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; a.kind = kind let body = newNodeI(nkStmtList, info) let procname = case kind - of attachedAsgn: getIdent":Asgn" - of attachedSink: getIdent":Sink" - of attachedDeepCopy: getIdent":DeepCopy" - of attachedDestructor: getIdent":Destroy" + of attachedAsgn: getIdent"=" + of attachedSink: getIdent"=sink" + of attachedDeepCopy: getIdent"=deepcopy" + of attachedDestructor: getIdent"=destroy" result = newSym(skProc, procname, typ.owner, info) a.fn = result @@ -314,6 +314,24 @@ proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = let a = getAsgnOrLiftBody(c, dest.typ, dest.info) result = newAsgnCall(c, a, dest, src) +proc patchHead(n: PNode; alreadyPatched: var IntSet) = + when true: + if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1: + let s = n[0].sym + if sfFromGeneric in s.flags: + if not containsOrIncl(alreadyPatched, s.id): + patchHead(s.getBody, alreadyPatched) + let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) + template patch(op, field) = + if s.name.s == op and field != nil and field != s: + n.sons[0].sym = field + + patch "=sink", t.sink + patch "=", t.assignment + patch "=destroy", t.destructor + for x in n: + patchHead(x, alreadyPatched) + proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = ## In the semantic pass this is called in strategic places ## to ensure we lift assignment, destructors and moves properly. @@ -321,6 +339,20 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = if not newDestructors or not hasDestructor(typ): return let typ = typ.skipTypes({tyGenericInst, tyAlias}) # we generate the destructor first so that other operators can depend on it: - if typ.destructor == nil: liftBody(c, typ, attachedDestructor, info) - if typ.assignment == nil: liftBody(c, typ, attachedAsgn, info) - if typ.sink == nil: liftBody(c, typ, attachedSink, info) + var changed = false + if typ.destructor != nil and typ.destructor.magic == mAsgn: + typ.destructor = nil + if typ.destructor == nil: + liftBody(c, typ, attachedDestructor, info) + changed = true + if typ.assignment == nil: + liftBody(c, typ, attachedAsgn, info) + changed = true + if typ.sink == nil: + liftBody(c, typ, attachedSink, info) + changed = true + if changed: + var alreadyPatched = initIntSet() + patchHead(typ.destructor.getBody, alreadyPatched) + patchHead(typ.assignment.getBody, alreadyPatched) + patchHead(typ.sink.getBody, alreadyPatched) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 49dfbe8f4..2c80db52c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -664,6 +664,19 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, result, orig, callee, flags) of skTemplate: result = semTemplateExpr(c, result, callee, flags) else: + when false: + if callee.name.s[0] == '=' and result.len > 1: + # careful, do not skip tyDistinct here: + let t = result[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) + + proc patchHead(callee: PSym; name: string; field: PSym; result: PNode) = + if callee.name.s == name and field != nil: + result.sons[0].sym = field + + patchHead(callee, "=destroy", t.destructor, result) + patchHead(callee, "=sink", t.sink, result) + patchHead(callee, "=", t.assignment, result) + semFinishOperands(c, result) activate(c, result) fixAbstractType(c, result) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 88f1262ae..ba19e0865 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -147,8 +147,9 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = of "stripGenericParams": result = uninstantiate(operand).toNode(traitCall.info) of "supportsCopyMem": - let complexObj = containsGarbageCollectedRef(operand) or - hasDestructor(operand) + let t = operand.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) + let complexObj = containsGarbageCollectedRef(t) or + hasDestructor(t) result = newIntNodeT(ord(not complexObj), traitCall) else: localError(traitCall.info, "unknown trait") @@ -251,7 +252,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = semTypeOf(c, n.sons[1]) of mArrGet: result = semArrGet(c, n, flags) of mArrPut: result = semArrPut(c, n, flags) - of mAsgn: result = semAsgnOpr(c, n) + of mAsgn: + if n[0].sym.name.s == "=": + result = semAsgnOpr(c, n) + else: + result = n of mIsPartOf: result = semIsPartOf(c, n, flags) of mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: diff --git a/compiler/transf.nim b/compiler/transf.nim index c3d12dafe..b7383c9b8 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -365,16 +365,19 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n.sons[0].sons[0] = m.sons[0] result = PTransNode(n.sons[0]) + PNode(result).typ = n.typ of nkHiddenStdConv, nkHiddenSubConv, nkConv: var m = n.sons[0].sons[1] if m.kind == a or m.kind == b: # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n.sons[0].sons[1] = m.sons[0] result = PTransNode(n.sons[0]) + PNode(result).typ = n.typ else: if n.sons[0].kind == a or n.sons[0].kind == b: # addr ( deref ( x )) --> x result = PTransNode(n.sons[0].sons[0]) + PNode(result).typ = n.typ proc generateThunk(prc: PNode, dest: PType): PNode = ## Converts 'prc' into '(thunk, nil)' so that it's compatible with diff --git a/lib/system.nim b/lib/system.nim index 10560edaa..585c32556 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -300,10 +300,10 @@ when defined(nimArrIdx): x: S) {.noSideEffect, magic: "ArrPut".} proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} when defined(nimNewRuntime): - proc `=destroy`*[T](x: var T) {.inline.} = + proc `=destroy`*[T](x: var T) {.inline, magic: "Asgn".} = ## generic `destructor`:idx: implementation that can be overriden. discard - proc `=sink`*[T](x: var T; y: T) {.inline.} = + proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} = ## generic `sink`:idx: implementation that can be overriden. shallowCopy(x, y) diff --git a/tests/destructor/tbintree.nim b/tests/destructor/tbintree.nim deleted file mode 100644 index 12ba052cf..000000000 --- a/tests/destructor/tbintree.nim +++ /dev/null @@ -1,97 +0,0 @@ -discard """ - output: '''10.0 -60.0 -90.0 -120.0 -4 1''' - cmd: '''nim c --newruntime $file''' -""" - -import typetraits - -type - opt[T] = object - data: ptr T - -var - allocCount, deallocCount: int - -proc `=destroy`*[T](x: var opt[T]) = - if x.data != nil: - when not supportsCopyMem(T): - `=destroy`(x.data) - dealloc(x.data) - inc deallocCount - x.data = nil - -proc `=`*[T](a: var opt[T]; b: opt[T]) = - if a.data == b.data: return - if a.data != nil: - dealloc(a.data) - inc deallocCount - a.data = nil - if b.data != nil: - a.data = cast[type(a.data)](alloc(sizeof(T))) - inc allocCount - when supportsCopyMem(T): - copyMem(a.data, b.data, sizeof(T)) - else: - a.data[] = b.data[] - -proc `=sink`*[T](a: var opt[T]; b: opt[T]) = - if a.data != nil and a.data != b.data: - dealloc(a.data) - inc deallocCount - a.data = b.data - -proc createOpt*[T](x: T): opt[T] = - result.data = cast[type(result.data)](alloc(sizeof(T))) - inc allocCount - result.data[] = x - -template `[]`[T](x: opt[T]): T = - assert x.p != nil, "attempt to read from moved/destroyed value" - x.p[] - -template `?=`[T](it: untyped; x: opt[T]): bool = - template it: untyped {.inject.} = x.data[] - if x.data != nil: - true - else: - false - -type - Tree = object - data: float - le, ri: opt[Tree] - -proc createTree(data: float): Tree = - result.data = data - -proc insert(t: var opt[Tree]; newVal: float) = - if it ?= t: - if it.data > newVal: - insert(it.le, newVal) - elif it.data < newVal: - insert(it.ri, newVal) - else: - discard "already in the tree" - else: - t = createOpt(Tree(data: newVal)) - -proc write(t: opt[Tree]) = - if it ?= t: - write(it.le) - write stdout, it.data, "\n" - write(it.ri) - -proc main = - var t: opt[Tree] - insert t, 60.0 - insert t, 90.0 - insert t, 10.0 - insert t, 120.0 - write t - -main() -echo allocCount, " ", deallocCount diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim new file mode 100644 index 000000000..a6380a2d5 --- /dev/null +++ b/tests/destructor/topttree.nim @@ -0,0 +1,98 @@ +discard """ + output: '''10.0 +60.0 +90.0 +120.0 +4 1''' + cmd: '''nim c --newruntime $file''' +""" + +import typetraits + +type + opt[T] = object + data: ptr T + +var + allocCount, deallocCount: int + +proc `=destroy`*[T](x: var opt[T]) = + if x.data != nil: + when not supportsCopyMem(T): + `=destroy`(x.data[]) + dealloc(x.data) + inc deallocCount + x.data = nil + +proc `=`*[T](a: var opt[T]; b: opt[T]) = + if a.data == b.data: return + if a.data != nil: + dealloc(a.data) + inc deallocCount + a.data = nil + if b.data != nil: + a.data = cast[type(a.data)](alloc(sizeof(T))) + inc allocCount + when supportsCopyMem(T): + copyMem(a.data, b.data, sizeof(T)) + else: + a.data[] = b.data[] + +proc `=sink`*[T](a: var opt[T]; b: opt[T]) = + if a.data != nil and a.data != b.data: + dealloc(a.data) + inc deallocCount + a.data = b.data + +proc createOpt*[T](x: T): opt[T] = + result.data = cast[type(result.data)](alloc(sizeof(T))) + inc allocCount + result.data[] = x + +template `[]`[T](x: opt[T]): T = + assert x.p != nil, "attempt to read from moved/destroyed value" + x.p[] + +template `?=`[T](it: untyped; x: opt[T]): bool = + template it: untyped {.inject.} = x.data[] + if x.data != nil: + true + else: + false + +type + Tree = object + data: float + le, ri: opt[Tree] + +proc createTree(data: float): Tree = + result.data = data + +proc insert(t: var opt[Tree]; newVal: float) = + #if it ?= t: + if t.data != nil: + if newVal < t.data[].data: + insert(t.data[].le, newVal) + elif t.data[].data < newVal: + insert(t.data[].ri, newVal) + else: + discard "already in the tree" + else: + t = createOpt(Tree(data: newVal)) + +proc write(t: opt[Tree]) = + if it ?= t: + write(it.le) + write stdout, it.data, "\n" + write(it.ri) + +proc main = + var t: opt[Tree] + insert t, 60.0 + insert t, 90.0 + insert t, 10.0 + insert t, 120.0 + write t + +main() +echo allocCount, " ", deallocCount -- cgit 1.4.1-2-gfad0 From 4f2b79a380385b5811b1b05937e2957a2ead61c0 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 24 Oct 2017 09:58:07 +0200 Subject: topttree destructor finally works --- compiler/destroyer.nim | 18 +++++++----------- compiler/semasgn.nim | 29 ----------------------------- compiler/semexprs.nim | 13 ------------- tests/destructor/tcustomstrings.nim | 10 ++++++---- tests/destructor/topttree.nim | 6 +++--- 5 files changed, 16 insertions(+), 60 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index a868a64ba..36839bf0b 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -164,26 +164,22 @@ proc isHarmlessVar*(s: PSym; c: Con): bool = template interestingSym(s: PSym): bool = s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) -proc patchHead(n: PNode; alreadyPatched: var IntSet) = +proc patchHead(n: PNode) = if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1: let s = n[0].sym - if sfFromGeneric in s.flags or true: - if s.name.s[0] == '=' and not containsOrIncl(alreadyPatched, s.id): - patchHead(s.getBody, alreadyPatched) + if sfFromGeneric in s.flags and s.name.s[0] == '=' and + s.name.s in ["=sink", "=", "=destroy"]: + excl(s.flags, sfFromGeneric) + patchHead(s.getBody) let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) template patch(op, field) = - if s.name.s == op and field != nil: #and field != s: + if s.name.s == op and field != nil and field != s: n.sons[0].sym = field patch "=sink", t.sink patch "=", t.assignment patch "=destroy", t.destructor for x in n: - patchHead(x, alreadyPatched) - -template patchHead(n: PNode) = - when false: - var alreadyPatched = initIntSet() - patchHead(n, alreadyPatched) + patchHead(x) proc genSink(t: PType; dest: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias}) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 0454ea379..5ae9feec2 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -314,24 +314,6 @@ proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = let a = getAsgnOrLiftBody(c, dest.typ, dest.info) result = newAsgnCall(c, a, dest, src) -proc patchHead(n: PNode; alreadyPatched: var IntSet) = - when true: - if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1: - let s = n[0].sym - if sfFromGeneric in s.flags: - if not containsOrIncl(alreadyPatched, s.id): - patchHead(s.getBody, alreadyPatched) - let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) - template patch(op, field) = - if s.name.s == op and field != nil and field != s: - n.sons[0].sym = field - - patch "=sink", t.sink - patch "=", t.assignment - patch "=destroy", t.destructor - for x in n: - patchHead(x, alreadyPatched) - proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = ## In the semantic pass this is called in strategic places ## to ensure we lift assignment, destructors and moves properly. @@ -339,20 +321,9 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = if not newDestructors or not hasDestructor(typ): return let typ = typ.skipTypes({tyGenericInst, tyAlias}) # we generate the destructor first so that other operators can depend on it: - var changed = false - if typ.destructor != nil and typ.destructor.magic == mAsgn: - typ.destructor = nil if typ.destructor == nil: liftBody(c, typ, attachedDestructor, info) - changed = true if typ.assignment == nil: liftBody(c, typ, attachedAsgn, info) - changed = true if typ.sink == nil: liftBody(c, typ, attachedSink, info) - changed = true - if changed: - var alreadyPatched = initIntSet() - patchHead(typ.destructor.getBody, alreadyPatched) - patchHead(typ.assignment.getBody, alreadyPatched) - patchHead(typ.sink.getBody, alreadyPatched) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2c80db52c..49dfbe8f4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -664,19 +664,6 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, result, orig, callee, flags) of skTemplate: result = semTemplateExpr(c, result, callee, flags) else: - when false: - if callee.name.s[0] == '=' and result.len > 1: - # careful, do not skip tyDistinct here: - let t = result[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) - - proc patchHead(callee: PSym; name: string; field: PSym; result: PNode) = - if callee.name.s == name and field != nil: - result.sons[0].sym = field - - patchHead(callee, "=destroy", t.destructor, result) - patchHead(callee, "=sink", t.sink, result) - patchHead(callee, "=", t.assignment, result) - semFinishOperands(c, result) activate(c, result) fixAbstractType(c, result) diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim index 449a1e9f2..1a78df20b 100644 --- a/tests/destructor/tcustomstrings.nim +++ b/tests/destructor/tcustomstrings.nim @@ -8,6 +8,8 @@ after 20 20''' cmd: '''nim c --newruntime $file''' """ +{.this: self.} + type mystring = object len, cap: int @@ -46,10 +48,10 @@ proc `=`*(a: var mystring; b: mystring) = copyMem(a.data, b.data, a.cap+1) proc resize(self: var mystring) = - if cap == 0: cap = 8 - else: cap = (cap * 3) shr 1 - if data == nil: inc allocCount - data = cast[type(data)](realloc(data, cap + 1)) + if self.cap == 0: self.cap = 8 + else: self.cap = (self.cap * 3) shr 1 + if self.data == nil: inc allocCount + self.data = cast[type(data)](realloc(self.data, self.cap + 1)) proc add*(self: var mystring; c: char) = if self.len >= self.cap: resize(self) diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim index a6380a2d5..45f2f66e6 100644 --- a/tests/destructor/topttree.nim +++ b/tests/destructor/topttree.nim @@ -3,7 +3,7 @@ discard """ 60.0 90.0 120.0 -4 1''' +4 4''' cmd: '''nim c --newruntime $file''' """ @@ -18,8 +18,8 @@ var proc `=destroy`*[T](x: var opt[T]) = if x.data != nil: - when not supportsCopyMem(T): - `=destroy`(x.data[]) + #when not supportsCopyMem(T): + `=destroy`(x.data[]) dealloc(x.data) inc deallocCount x.data = nil -- cgit 1.4.1-2-gfad0 From 70ea45cdbaa9d26a7196ab2718f60c9ca77e2d12 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 29 Oct 2017 08:37:13 +0100 Subject: deprecated unary '<' --- changelog.md | 2 ++ compiler/ast.nim | 4 +-- compiler/canonicalizer.nim | 6 ++-- compiler/ccgcalls.nim | 6 ++-- compiler/ccgexprs.nim | 4 +-- compiler/ccgstmts.nim | 14 ++++---- compiler/ccgtypes.nim | 2 +- compiler/ccgutils.nim | 4 +-- compiler/cgen.nim | 2 +- compiler/dfa.nim | 4 +-- compiler/docgen.nim | 6 ++-- compiler/evalffi.nim | 4 +-- compiler/evaltempl.nim | 2 +- compiler/forloops.nim | 4 +-- compiler/guards.nim | 2 +- compiler/hlo.nim | 4 +-- compiler/jsgen.nim | 6 ++-- compiler/jstypes.nim | 2 +- compiler/lexer.nim | 2 +- compiler/lookups.nim | 2 +- compiler/lowerings.nim | 4 +-- compiler/parampatterns.nim | 6 ++-- compiler/patterns.nim | 18 +++++----- compiler/pragmas.nim | 2 +- compiler/renderer.nim | 2 +- compiler/reorder.nim | 2 +- compiler/rodread.nim | 2 +- compiler/sem.nim | 2 +- compiler/semasgn.nim | 6 ++-- compiler/semcall.nim | 4 +-- compiler/semdestruct.nim | 2 +- compiler/semexprs.nim | 20 +++++------ compiler/semfields.nim | 2 +- compiler/seminst.nim | 8 ++--- compiler/semmacrosanity.nim | 4 +-- compiler/semobjconstr.nim | 10 +++--- compiler/semparallel.nim | 24 ++++++------- compiler/sempass2.nim | 48 +++++++++++++------------- compiler/semstmts.nim | 14 ++++---- compiler/semtempl.nim | 8 ++--- compiler/semtypes.nim | 8 ++--- compiler/semtypinst.nim | 16 ++++----- compiler/sighashes.nim | 6 ++-- compiler/sigmatch.nim | 16 ++++----- compiler/transf.nim | 10 +++--- compiler/trees.nim | 2 +- compiler/types.nim | 4 +-- compiler/typesrenderer.nim | 20 +++++------ compiler/vm.nim | 23 ++++++------- compiler/vmdeps.nim | 6 ++-- compiler/vmgen.nim | 22 ++++++------ compiler/vmmarshal.nim | 4 +-- compiler/writetracking.nim | 2 +- lib/pure/collections/sequtils.nim | 34 +++++++++---------- lib/system.nim | 71 ++++++++++++++++++++------------------- todo.txt | 3 -- 56 files changed, 259 insertions(+), 258 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/changelog.md b/changelog.md index ebd454ab7..6dea3ee63 100644 --- a/changelog.md +++ b/changelog.md @@ -20,3 +20,5 @@ recursive types can be created across module boundaries. See [package level objects](https://nim-lang.org/docs/manual.html#package-level-objects) for more information. +- The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages + use the ``pred`` proc. diff --git a/compiler/ast.nim b/compiler/ast.nim index aa6af7e3f..43aa3e484 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1604,10 +1604,10 @@ proc hasPattern*(s: PSym): bool {.inline.} = result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty iterator items*(n: PNode): PNode = - for i in 0.. = nkNone and n.kind <= nkNilLit diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 6972f5acf..d1669a06c 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -102,7 +102,7 @@ proc hashTree(c: var MD5Context, n: PNode) = of nkStrLit..nkTripleStrLit: c &= n.strVal else: - for i in 0.. = 1: result = shallowCopy(n) - for i in 0 .. < n.len: + for i in 0 ..< n.len: result.sons[i] = canon(n.sons[i]) elif n.kind == nkSym and n.sym.kind == skLet and n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin + diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 9491eef83..2bffaa173 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -36,7 +36,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode = # we apply the last pattern first, so that pattern overriding is possible; # however the resulting AST would better not trigger the old rule then # anymore ;-) - for i in countdown( 0: add(r.res, ", ") genOtherArg(p, n, k, typ, generated, r) inc i @@ -1528,7 +1528,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = of tyTuple: if p.target == targetJS: result = rope("{") - for i in 0.. 0: add(result, ", ") addf(result, "Field$1: $2", [i.rope, createVar(p, t.sons[i], false)]) @@ -1536,7 +1536,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = if indirect: result = "[$1]" % [result] else: result = rope("array(") - for i in 0.. 0: add(result, ", ") add(result, createVar(p, t.sons[i], false)) add(result, ")") diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 0d5b29ace..d9df04e4b 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -84,7 +84,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) = proc genTupleFields(p: PProc, typ: PType): Rope = var s: Rope = nil - for i in 0 .. 0: add(s, ", " & tnl) s.addf("{kind: 1, offset: \"Field$1\", len: 0, " & "typ: $2, name: \"Field$1\", sons: null}", diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 895848e77..2ae2176de 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -693,7 +693,7 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = proc newString(s: cstring, len: int): string = ## XXX, how come there is no support for this? result = newString(len) - for i in 0 .. = 2: - for i in 1.. MaxStackSize-1: internalError(p.info, "parameter pattern too complex") @@ -152,7 +152,7 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis = # indirect call: assume side effect: return seSideEffect # we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args) - for i in 0 .. = 2: - for i in 1 .. < params.len: + for i in 1 ..< params.len: let param = params.sons[i].sym if whichAlias(param) != aqNone: return true @@ -237,7 +237,7 @@ proc addToArgList(result, n: PNode) = if n.typ != nil and n.typ.kind != tyStmt: if n.kind != nkArgList: result.add(n) else: - for i in 0 .. 0: gsub(g, n.sons[0]) - for i in 1 .. PRodReader but I'll leave # this for later versions if benchmarking shows the linear search causes # problems: - for i in 0 .. 1: - for i in 1 .. 1: resetIdTable(cl.symMap) diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index a6024a42f..fe9bb6c8d 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -42,7 +42,7 @@ proc annotateType*(n: PNode, t: PType) = of nkObjConstr: let x = t.skipTypes(abstractPtrs) n.typ = t - for i in 1 .. = x.len: globalError n.info, "invalid field at index " & $i else: annotateType(n.sons[i], x.sons[i]) elif x.kind == tyProc and x.callConv == ccClosure: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index b331d05a1..c73b042fe 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -42,7 +42,7 @@ proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) = proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode = # Returns the assignment nkExprColonExpr node or nil let fieldId = field.name.id - for i in 1 .. = 0 and c.locals[s].stride != nil: localError(n.info, "invalid usage of counter after increment") else: - for i in 0 .. = 0 and c.locals[s].stride != nil: result = c.locals[s].stride.intVal else: - for i in 0 .. 0: result = shallowCopy(n) - for i in 0 .. 1: addFact(c.guards, canon(branch.sons[0])) - for i in 0 .. 0: result = shallowCopy(n) - for i in 0 .. < n.len: + for i in 0 ..< n.len: result.sons[i] = transformSlices(n.sons[i]) else: result = n @@ -415,7 +415,7 @@ proc transformSlices(n: PNode): PNode = proc transformSpawn(owner: PSym; n, barrier: PNode): PNode proc transformSpawnSons(owner: PSym; n, barrier: PNode): PNode = result = shallowCopy(n) - for i in 0 .. < n.len: + for i in 0 ..< n.len: result.sons[i] = transformSpawn(owner, n.sons[i], barrier) proc transformSpawn(owner: PSym; n, barrier: PNode): PNode = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 17d9c9840..5add78268 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -248,7 +248,7 @@ type TIntersection = seq[tuple[id, count: int]] # a simple count table proc addToIntersection(inter: var TIntersection, s: int) = - for j in 0.. 1: addFact(tracked.guards, branch.sons[0]) setLen(tracked.init, oldState) - for i in 0 .. = 3): diff --git a/compiler/trees.nim b/compiler/trees.nim index c77dab349..7efefdc2e 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -98,7 +98,7 @@ proc isDeepConstExpr*(n: PNode): bool = of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, nkRange: - for i in ord(n.kind == nkObjConstr) .. 0 result = "proc(" - for i in 1 .. = 2 result = renderType(n[0]) & '[' - for i in 1 .. n.safeLen and sym.typ.len > 1: globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [ - n.renderTree, - $ 0 and i < sonsLen(fntyp): # let paramType = fntyp.n.sons[i] # if paramType.typ.isCompileTimeOnly: continue @@ -995,7 +995,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = let n = n[1].skipConv let x = c.getTempRange(n.len, slotTempUnknown) internalAssert n.kind == nkBracket - for i in 0.. 0: s.add(", ") s.add("\"Field" & $i) s.add("\": ") @@ -90,7 +90,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) = s.add("}") of tySet: s.add("[") - for i in 0.. 0: s.add(", ") if a[i].kind == nkRange: var x = copyNode(a[i][0]) diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index fe71e5b31..577db613d 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -123,7 +123,7 @@ proc returnsNewExpr*(n: PNode): NewLocation = of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt: result = newLit - for i in ord(n.kind == nkObjConstr) .. 0: inc(stride) - for i in 0 .. 0: extra -= 1 inc(last) result[i] = newSeq[T]() - for g in first .. ["142", "242", "342", "442"] ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead. - for i in 0 .. ["142", "242", "342", "442"] ## - for i in 0 .. ["142", "242", "342", "442"] ## - for i in 0 .. 10) ## assert floats == @[13.0, 12.5, 10.1] var pos = 0 - for i in 0 .. 0 1 2 3 4 5 6 7 8 9 ## ## Semantically this is the same as ``pred``. + ## + ## **Deprecated since version 0.18.0**. For the common excluding range + ## write ``0 ..< 10`` instead of ``0 .. < 10`` (look at the spacing). + ## For `` requires 'mixin' annotation for procs! - make 'nil' work for 'add': - resizeString - incrSeq -- cgit 1.4.1-2-gfad0 From d52a1061b35bbd2abfbd062b08023d986dbafb3c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 29 Oct 2017 19:46:17 +0100 Subject: work in progress: new implementation for 'a[^1]' --- changelog.md | 6 +++ compiler/ast.nim | 13 ++----- compiler/cgendata.nim | 2 +- compiler/parser.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semobjconstr.nim | 15 ++++--- compiler/semstmts.nim | 2 +- compiler/semtypes.nim | 4 +- lib/core/macros.nim | 23 +++++++---- lib/pure/asyncmacro.nim | 14 +++---- lib/pure/random.nim | 2 +- lib/system.nim | 99 +++++++++++++++++++++++------------------------ todo.txt | 1 - 13 files changed, 96 insertions(+), 89 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/changelog.md b/changelog.md index 6dea3ee63..5740f8589 100644 --- a/changelog.md +++ b/changelog.md @@ -22,3 +22,9 @@ for more information. - The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages use the ``pred`` proc. +- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are + implemented. These are now implemented purely in ``system.nim`` without compiler + support. ``system.Slice`` now takes 2 generic parameters so that it can + take ``BackwardsIndex`` indices. ``BackwardsIndex`` is produced by ``system.^``. + This means if you overload ``[]`` or ``[]=`` you need to ensure they also work + with ``system.BackwardsIndex`` (if applicable for the accessors). diff --git a/compiler/ast.nim b/compiler/ast.nim index 43aa3e484..e6d967dde 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1017,16 +1017,11 @@ proc add*(father, son: PNode) = type Indexable = PNode | PType -template `[]`*(n: Indexable, i: int): Indexable = - n.sons[i] +template `[]`*(n: Indexable, i: int): Indexable = n.sons[i] +template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x -template `-|`*(b, s: untyped): untyped = - (if b >= 0: b else: s.len + b) - -# son access operators with support for negative indices -template `{}`*(n: Indexable, i: int): untyped = n[i -| n] -template `{}=`*(n: Indexable, i: int, s: Indexable) = - n.sons[i -| n] = s +template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int] +template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x when defined(useNodeIds): const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879 diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index be087095f..19ab2fe50 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -154,7 +154,7 @@ proc includeHeader*(this: BModule; header: string) = proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} = # section in the current block - result = p.blocks[^1].sections[s] + result = p.blocks[p.blocks.len-1].sections[s] proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} = # top level proc sections diff --git a/compiler/parser.nim b/compiler/parser.nim index e0885c388..113922189 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1905,7 +1905,7 @@ proc parseVariable(p: var TParser): PNode = #| variable = (varTuple / identColonEquals) colonBody? indAndComment if p.tok.tokType == tkParLe: result = parseVarTuple(p) else: result = parseIdentColonEquals(p, {withPragma, withDot}) - result{-1} = postExprBlocks(p, result{-1}) + result[^1] = postExprBlocks(p, result[^1]) indAndComment(p, result) proc parseBind(p: var TParser, k: TNodeKind): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3cba0abd8..195625489 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1665,7 +1665,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = # We transform the do block into a template with a param for # each interpolation. We'll pass this template to getAst. var - quotedBlock = n{-1} + quotedBlock = n[^1] op = if n.len == 3: expectString(c, n[1]) else: "``" quotes = newSeq[PNode](1) # the quotes will be added to a nkCall statement diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index c73b042fe..56d160aa4 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -78,13 +78,13 @@ proc caseBranchMatchesExpr(branch, matched: PNode): bool = proc pickCaseBranch(caseExpr, matched: PNode): PNode = # XXX: Perhaps this proc already exists somewhere - let endsWithElse = caseExpr{-1}.kind == nkElse + let endsWithElse = caseExpr[^1].kind == nkElse for i in 1 .. caseExpr.len - 1 - int(endsWithElse): if caseExpr[i].caseBranchMatchesExpr(matched): return caseExpr[i] if endsWithElse: - return caseExpr{-1} + return caseExpr[^1] iterator directFieldsInRecList(recList: PNode): PNode = # XXX: We can remove this case by making all nkOfBranch nodes @@ -136,17 +136,20 @@ proc semConstructFields(c: PContext, recNode: PNode, of nkRecCase: template fieldsPresentInBranch(branchIdx: int): string = - fieldsPresentInInitExpr(recNode[branchIdx]{-1}, initExpr) + let branch = recNode[branchIdx] + let fields = branch[branch.len - 1] + fieldsPresentInInitExpr(fields, initExpr) template checkMissingFields(branchNode: PNode) = - checkForMissingFields(branchNode{-1}, initExpr) + let fields = branchNode[branchNode.len - 1] + checkForMissingFields(fields, initExpr) let discriminator = recNode.sons[0]; internalAssert discriminator.kind == nkSym var selectedBranch = -1 for i in 1 ..< recNode.len: - let innerRecords = recNode[i]{-1} + let innerRecords = recNode[i][^1] let status = semConstructFields(c, innerRecords, initExpr, flags) if status notin {initNone, initUnknown}: mergeInitStatus(result, status) @@ -250,7 +253,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n.sons[0], nil) result = newNodeIT(nkObjConstr, n.info, t) for child in n: result.add child - + t = skipTypes(t, {tyGenericInst, tyAlias}) if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) if t.kind != tyObject: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 55d84c6fc..540ef4c07 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -823,7 +823,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = a.sons[0] = newSymNode(s) proc checkCovariantParamsUsages(genericType: PType) = - var body = genericType{-1} + var body = genericType[^1] proc traverseSubTypes(t: PType): bool = template error(msg) = localError(genericType.sym.info, msg) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index cebbbad47..18ea87341 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1618,8 +1618,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = var a = n.sons[i] if a.kind != nkIdentDefs: illFormedAst(n) let L = a.len - var def = a{-1} - let constraint = a{-2} + var def = a[^1] + let constraint = a[^2] var typ: PType if constraint.kind != nkEmpty: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d3f541153..fc5b5bfb7 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -129,13 +129,6 @@ const nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit} -proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.} - ## get `n`'s `i`'th child. - -proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild", - noSideEffect.} - ## set `n`'s `i`'th child to `child`. - proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} ## constructs an identifier from the string `s` @@ -162,6 +155,20 @@ proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} = proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.} ## returns the number of children of `n`. +proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.} + ## get `n`'s `i`'th child. + +proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int] + ## get `n`'s `i`'th child. + +proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild", + noSideEffect.} + ## set `n`'s `i`'th child to `child`. + +proc `[]=`*(n: NimNode, i: BackwardsIndex, child: NimNode) = + ## set `n`'s `i`'th child to `child`. + n[n.len - i.int] = child + proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable, noSideEffect, locks: 0.} ## Adds the `child` to the `father` node. Returns the @@ -1104,7 +1111,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = else: result = false -proc hasArgOfName* (params: NimNode; name: string): bool {.compiletime.}= +proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}= ## Search nnkFormalParams for an argument. assert params.kind == nnkFormalParams for i in 1 ..< params.len: diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 6e7d7993f..981190211 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -61,14 +61,14 @@ proc generateExceptionCheck(futSym, else: var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[] let errorNode = newDotExpr(futSym, newIdentNode("error")) - for i in 1 .. FormalParams (3) -> IdentDefs, the parameter (i) -> # parameter type (1). result[0][3][i][1] = splitParamType(result[0][3][i][1], async=false) @@ -521,7 +521,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) = result[1] = prc.copyNimTree() if result[1][3][0].kind == nnkBracketExpr: result[1][3][0][1] = splitParamType(result[1][3][0][1], async=true) - for i in 1 .. FormalParams (3) -> IdentDefs, the parameter (i) -> # parameter type (1). result[1][3][i][1] = splitParamType(result[1][3][i][1], async=true) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 27fbfad45..2c406faa1 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -93,7 +93,7 @@ proc random*(max: float): float {.benign.} = let u = (0x3FFu64 shl 52u64) or (x shr 12u64) result = (cast[float](u) - 1.0) * max -proc random*[T](x: Slice[T]): T = +proc random*[T](x: Slice[T, T]): T = ## For a slice `a .. b` returns a value in the range `a .. b-1`. result = T(random(x.b - x.a)) + x.a diff --git a/lib/system.nim b/lib/system.nim index 3b7a93eca..3ea2d8ef8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -308,13 +308,14 @@ when defined(nimArrIdx): shallowCopy(x, y) type - Slice*[T] = object ## builtin slice type - a*, b*: T ## the bounds + Slice*[T, U] = object ## builtin slice type + a*: T ## the lower bound (inclusive) + b*: U ## the upper bound (inclusive) when defined(nimalias): {.deprecated: [TSlice: Slice].} -proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} = +proc `..`*[T, U](a: T, b: U): Slice[T, U] {.noSideEffect, inline, magic: "DotDot".} = ## `slice`:idx: operator that constructs an interval ``[a, b]``, both `a` ## and `b` are inclusive. Slices can also be used in the set constructor ## and in ordinal case statements, but then they are special-cased by the @@ -322,7 +323,7 @@ proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} = result.a = a result.b = b -proc `..`*[T](b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} = +proc `..`*[T](b: T): Slice[T, T] {.noSideEffect, inline, magic: "DotDot".} = ## `slice`:idx: operator that constructs an interval ``[default(T), b]`` result.b = b @@ -1168,7 +1169,7 @@ proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.} ## is achieved by reversing the parameters for ``contains``; ``in`` then ## passes its arguments in reverse order. -proc contains*[T](s: Slice[T], value: T): bool {.noSideEffect, inline.} = +proc contains*[T](s: Slice[T, T], value: T): bool {.noSideEffect, inline.} = ## Checks if `value` is within the range of `s`; returns true iff ## `value >= s.a and value <= s.b` ## @@ -2087,7 +2088,7 @@ proc clamp*[T](x, a, b: T): T = if x > b: return b return x -proc len*[T: Ordinal](x: Slice[T]): int {.noSideEffect, inline.} = +proc len*[T: Ordinal](x: Slice[T, T]): int {.noSideEffect, inline.} = ## length of ordinal slice, when x.b < x.a returns zero length ## ## .. code-block:: Nim @@ -2155,7 +2156,7 @@ iterator items*(E: typedesc[enum]): E = for v in low(E)..high(E): yield v -iterator items*[T](s: Slice[T]): T = +iterator items*[T](s: Slice[T, T]): T = ## iterates over the slice `s`, yielding each value between `s.a` and `s.b` ## (inclusively). for x in s.a..s.b: @@ -3414,14 +3415,13 @@ proc `/`*(x, y: int): float {.inline, noSideEffect.} = ## integer division that results in a float. result = toFloat(x) / toFloat(y) -proc `^`*[T](x: int; y: openArray[T]): int {.noSideEffect, magic: "Roof".} -proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} = +type + BackwardsIndex* = distinct int ## type that is constructed by ``^`` for + ## reversed array accesses. + +template `^`*(x: int): BackwardsIndex = BackwardsIndex(x) ## builtin `roof`:idx: operator that can be used for convenient array access. - ## ``a[^x]`` is rewritten to ``a[a.len-x]``. However currently the ``a`` - ## expression must not have side effects for this to compile. Note that since - ## this is a builtin, it automatically works for all kinds of - ## overloaded ``[]`` or ``[]=`` accessors. - discard + ## ``a[^x]`` is a shortcut for ``a[a.len-x]``. template `..^`*(a, b: untyped): untyped = ## a shortcut for '.. ^' to avoid the common gotcha that a space between @@ -3453,17 +3453,20 @@ template spliceImpl(s, a, L, b: untyped): untyped = # fill the hole: for i in 0 ..< b.len: s[a+i] = b[i] +template `^^`(s, i: untyped): untyped = + (when i is BackwardsIndex: s.len - int(i) else: int(i)) + when hasAlloc or defined(nimscript): - proc `[]`*(s: string, x: Slice[int]): string {.inline.} = + proc `[]`*[T, U](s: string, x: Slice[T, U]): string {.inline.} = ## slice operation for strings. ## returns the inclusive range [s[x.a], s[x.b]]: ## ## .. code-block:: nim ## var s = "abcdef" ## assert s[1..3] == "bcd" - result = s.substr(x.a, x.b) + result = s.substr(s ^^ x.a, s ^^ x.b) - proc `[]=`*(s: var string, x: Slice[int], b: string) = + proc `[]=`*[T, U](s: var string, x: Slice[T, U], b: string) = ## slice assignment for strings. If ## ``b.len`` is not exactly the number of elements that are referred to ## by `x`, a `splice`:idx: is performed: @@ -3472,75 +3475,69 @@ when hasAlloc or defined(nimscript): ## var s = "abcdef" ## s[1 .. ^2] = "xyz" ## assert s == "axyzf" - var a = x.a - var L = x.b - a + 1 + var a = s ^^ x.a + var L = (s ^^ x.b) - a + 1 if L == b.len: for i in 0..`_. diff --git a/todo.txt b/todo.txt index 7c2a9088d..c9f64bd8b 100644 --- a/todo.txt +++ b/todo.txt @@ -3,7 +3,6 @@ version 1.0 battle plan - make FlowVar compatible to Futures - remove 'mod x' type rule -- implement x[^1] differently, no compiler magic - fix "high priority" bugs - try to fix as many compiler crashes as reasonable -- cgit 1.4.1-2-gfad0 From f1dab3908699aef2978b428ed9c15086a2c4bf8c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 29 Oct 2017 20:36:07 +0100 Subject: remove old implementation of the roof operator; make tests green again; close #6292 --- compiler/semdata.nim | 1 - compiler/semexprs.nim | 11 +---------- compiler/semmagic.nim | 32 +------------------------------- compiler/semtempl.nim | 18 ------------------ lib/impure/nre.nim | 36 +++++++++++++++++------------------- lib/pure/matchers.nim | 2 +- lib/system.nim | 2 +- tests/array/troof1.nim | 27 +-------------------------- tests/array/troof2.nim | 10 ---------- tests/array/troof3.nim | 5 ++--- tests/array/troof4.nim | 37 ------------------------------------- tests/stdlib/nre/captures.nim | 6 +++--- tests/stdlib/nre/find.nim | 2 +- 13 files changed, 28 insertions(+), 161 deletions(-) delete mode 100644 tests/array/troof2.nim delete mode 100644 tests/array/troof4.nim (limited to 'compiler/semexprs.nim') diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b6b5db101..3e57d1104 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -37,7 +37,6 @@ type # in standalone ``except`` and ``finally`` next*: PProcCon # used for stacking procedure contexts wasForwarded*: bool # whether the current proc has a separate header - bracketExpr*: PNode # current bracket expression (for ^ support) mapping*: TIdTable TMatchedConcept* = object diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 195625489..c3aead18e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1189,7 +1189,6 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = tyCString: if n.len != 2: return nil n.sons[0] = makeDeref(n.sons[0]) - c.p.bracketExpr = n.sons[0] for i in countup(1, sonsLen(n) - 1): n.sons[i] = semExprWithType(c, n.sons[i], flags*{efInTypeof, efDetermineType}) @@ -1210,7 +1209,6 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = of tyTuple: if n.len != 2: return nil n.sons[0] = makeDeref(n.sons[0]) - c.p.bracketExpr = n.sons[0] # [] operator for tuples requires constant expression: n.sons[1] = semConstExpr(c, n.sons[1]) if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias}).kind in @@ -1248,17 +1246,13 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = of skType: result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) else: - c.p.bracketExpr = n.sons[0] - else: - c.p.bracketExpr = n.sons[0] + discard proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = - let oldBracketExpr = c.p.bracketExpr result = semSubscript(c, n, flags) if result == nil: # overloaded [] operator: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) - c.p.bracketExpr = oldBracketExpr proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = var id = considerQuotedIdent(a[1], a) @@ -1330,7 +1324,6 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = of nkBracketExpr: # a[i] = x # --> `[]=`(a, i, x) - let oldBracketExpr = c.p.bracketExpr a = semSubscript(c, a, {efLValue}) if a == nil: result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") @@ -1340,9 +1333,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = return n else: result = semExprNoType(c, result) - c.p.bracketExpr = oldBracketExpr return result - c.p.bracketExpr = oldBracketExpr of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index ba19e0865..d721f42ab 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -38,9 +38,7 @@ proc skipAddr(n: PNode): PNode {.inline.} = proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = result = newNodeI(nkBracketExpr, n.info) for i in 1..\w)").captures["letter"] == "a"`` ## - ``"abc".match(re"(\w)\w").captures[-1] == "ab"`` ## - ## ``captureBounds[]: Option[Slice[int]]`` + ## ``captureBounds[]: Option[Slice[int, int]]`` ## gets the bounds of the given capture according to the same rules as ## the above. If the capture is not filled, then ``None`` is returned. ## The bounds are both inclusive. @@ -167,7 +167,7 @@ type ## ``match: string`` ## the full text of the match. ## - ## ``matchBounds: Slice[int]`` + ## ``matchBounds: Slice[int, int]`` ## the bounds of the match, as in ``captureBounds[]`` ## ## ``(captureBounds|captures).toTable`` @@ -182,9 +182,9 @@ type ## Not nil. str*: string ## The string that was matched against. ## Not nil. - pcreMatchBounds: seq[Slice[cint]] ## First item is the bounds of the match - ## Other items are the captures - ## `a` is inclusive start, `b` is exclusive end + pcreMatchBounds: seq[Slice[cint, cint]] ## First item is the bounds of the match + ## Other items are the captures + ## `a` is inclusive start, `b` is exclusive end Captures* = distinct RegexMatch CaptureBounds* = distinct RegexMatch @@ -251,13 +251,13 @@ proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(p proc captures*(pattern: RegexMatch): Captures = return Captures(pattern) -proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int]] = +proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int, int]] = let pattern = RegexMatch(pattern) if pattern.pcreMatchBounds[i + 1].a != -1: let bounds = pattern.pcreMatchBounds[i + 1] return some(int(bounds.a) .. int(bounds.b-1)) else: - return none(Slice[int]) + return none(Slice[int, int]) proc `[]`*(pattern: Captures, i: int): string = let pattern = RegexMatch(pattern) @@ -272,10 +272,10 @@ proc `[]`*(pattern: Captures, i: int): string = proc match*(pattern: RegexMatch): string = return pattern.captures[-1] -proc matchBounds*(pattern: RegexMatch): Slice[int] = +proc matchBounds*(pattern: RegexMatch): Slice[int, int] = return pattern.captureBounds[-1].get -proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int]] = +proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int, int]] = let pattern = RegexMatch(pattern) return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)] @@ -295,13 +295,13 @@ proc toTable*(pattern: Captures, default: string = nil): Table[string, string] = result = initTable[string, string]() toTableImpl(nextVal == nil) -proc toTable*(pattern: CaptureBounds, default = none(Slice[int])): - Table[string, Option[Slice[int]]] = - result = initTable[string, Option[Slice[int]]]() +proc toTable*(pattern: CaptureBounds, default = none(Slice[int, int])): + Table[string, Option[Slice[int, int]]] = + result = initTable[string, Option[Slice[int, int]]]() toTableImpl(nextVal.isNone) template itemsImpl(cond: untyped) {.dirty.} = - for i in 0 .. foo)(?bar)?")) check(ex1.captureBounds["foo"] == some(0..2)) - check(ex1.captureBounds["bar"] == none(Slice[int])) + check(ex1.captureBounds["bar"] == none(Slice[int, int])) test "capture count": let ex1 = re("(?foo)(?bar)?") @@ -42,7 +42,7 @@ suite "captures": test "named capture table": let ex1 = "foo".find(re("(?foo)(?bar)?")) check(ex1.captures.toTable == {"foo" : "foo", "bar" : nil}.toTable()) - check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable()) + check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int, int])}.toTable()) check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable()) let ex2 = "foobar".find(re("(?foo)(?bar)?")) @@ -51,7 +51,7 @@ suite "captures": test "capture sequence": let ex1 = "foo".find(re("(?foo)(?bar)?")) check(ex1.captures.toSeq == @["foo", nil]) - check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])]) + check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int, int])]) check(ex1.captures.toSeq("") == @["foo", ""]) let ex2 = "foobar".find(re("(?foo)(?bar)?")) diff --git a/tests/stdlib/nre/find.nim b/tests/stdlib/nre/find.nim index caa953ff4..c37ac56ba 100644 --- a/tests/stdlib/nre/find.nim +++ b/tests/stdlib/nre/find.nim @@ -12,7 +12,7 @@ suite "find": test "find bounds": check(toSeq(findIter("1 2 3 4 5 ", re" ")).map( - proc (a: RegexMatch): Slice[int] = a.matchBounds + proc (a: RegexMatch): Slice[int, int] = a.matchBounds ) == @[1..1, 3..3, 5..5, 7..7, 9..9]) test "overlapping find": -- cgit 1.4.1-2-gfad0 From d7a896f19dfc7ff2ad22b78ef03e4db73332fc7e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 30 Oct 2017 00:27:30 +0100 Subject: breaking change: 'and' and 'mod' do not produce a subrange type anymore; fixes #5854 --- changelog.md | 3 ++ compiler/semexprs.nim | 1 - compiler/semfold.nim | 110 --------------------------------------------- doc/manual/types.txt | 19 -------- tests/arithm/tsubrange.nim | 13 ++++++ todo.txt | 1 - 6 files changed, 16 insertions(+), 131 deletions(-) create mode 100644 tests/arithm/tsubrange.nim (limited to 'compiler/semexprs.nim') diff --git a/changelog.md b/changelog.md index 5740f8589..3854d168b 100644 --- a/changelog.md +++ b/changelog.md @@ -28,3 +28,6 @@ take ``BackwardsIndex`` indices. ``BackwardsIndex`` is produced by ``system.^``. This means if you overload ``[]`` or ``[]=`` you need to ensure they also work with ``system.BackwardsIndex`` (if applicable for the accessors). +- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This + turned out to be more harmful than helpful and the language is simpler + without this special typing rule. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c3aead18e..2a4d5a620 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -550,7 +550,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 d961a47a3..d2824d1a6 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -137,116 +137,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 4d66bf664..8361ec1f2 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -137,25 +137,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/tests/arithm/tsubrange.nim b/tests/arithm/tsubrange.nim new file mode 100644 index 000000000..6c6daebe2 --- /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 \ No newline at end of file diff --git a/todo.txt b/todo.txt index c9f64bd8b..df95b05ab 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,6 @@ version 1.0 battle plan ======================= - make FlowVar compatible to Futures -- remove 'mod x' type rule - fix "high priority" bugs - try to fix as many compiler crashes as reasonable -- cgit 1.4.1-2-gfad0 From a155130cf2a1ba57501f5b22e4b6818fbc2bd0ae Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 30 Oct 2017 15:37:34 +0100 Subject: fixes #1860; macro generating a wrong nkSymChoice node --- compiler/lookups.nim | 12 ++++++++++-- compiler/semexprs.nim | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'compiler/semexprs.nim') diff --git a/compiler/lookups.nim b/compiler/lookups.nim index e41fe17f6..65cf504cf 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -46,7 +46,11 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent = of nkSym: id.add(x.sym.name.s) else: handleError(n, origin) result = getIdent(id) - of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name + of nkOpenSymChoice, nkClosedSymChoice: + if n[0].kind == nkSym: + result = n.sons[0].sym.name + else: + handleError(n, origin) else: handleError(n, origin) @@ -379,7 +383,11 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = result = errorSym(c, n.sons[1]) of nkClosedSymChoice, nkOpenSymChoice: o.mode = oimSymChoice - result = n.sons[0].sym + if n[0].kind == nkSym: + result = n.sons[0].sym + else: + o.mode = oimDone + return nil o.symChoiceIndex = 1 o.inSymChoice = initIntSet() incl(o.inSymChoice, result.id) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2a4d5a620..7a16f495a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2097,7 +2097,7 @@ proc shouldBeBracketExpr(n: PNode): bool = let b = a[0] if b.kind in nkSymChoices: for i in 0..