diff options
39 files changed, 445 insertions, 244 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 5cc608c2e..a21d9f738 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -216,7 +216,7 @@ type nkEnumFieldDef, # `ident = expr` in an enumeration nkArgList, # argument list nkPattern, # a special pattern; used for matching - nkReturnToken, # token used for interpretation + nkHiddenTryStmt, # token used for interpretation nkClosure, # (prc, env)-pair (internally used for code gen) nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) @@ -227,7 +227,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # already 34 flags! + TSymFlag* = enum # already 36 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -278,6 +278,8 @@ type sfGenSym # symbol is 'gensym'ed; do not add to symbol table sfNonReloadable # symbol will be left as-is when hot code reloading is on - # meaning that it won't be renamed and/or changed in any way + sfGeneratedOp # proc is a generated '='; do not inject destructors in it + TSymFlags* = set[TSymFlag] @@ -478,7 +480,7 @@ type nfExecuteOnReload # A top-level statement that will be executed during reloads TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~38) tfVarargs, # procedure has C styled varargs # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects @@ -535,6 +537,8 @@ type tfCovariant # covariant generic param mimicing a ptr type tfWeakCovariant # covariant generic param mimicing a seq/array type tfContravariant # contravariant generic param + tfCheckedForDestructor # type was checked for having a destructor. + # If it has one, t.destructor is not nil. TTypeFlags* = set[TTypeFlag] @@ -640,7 +644,7 @@ type mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, - mDefault, mReset, + mDefault, mAccessEnv, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 839c4645d..4320ffea3 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -1002,7 +1002,7 @@ proc iiTablePut(t: var TIITable, key, val: int) = iiTableRawInsert(t.data, key, val) inc(t.counter) -proc isAddrNode*(n: PNode): bool = +proc isAddrNode*(n: PNode): bool = case n.kind of nkAddr, nkHiddenAddr: true of nkCallKinds: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 28aa875bc..4026a429e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1967,11 +1967,14 @@ proc genDestroy(p: BProc; n: PNode) = [rdLoc(a), getTypeDesc(p.module, t.lastSon)]) else: discard "nothing to do" else: + let t = n[1].typ.skipTypes(abstractVar) + if t.destructor != nil and t.destructor.magic != mDestroy: + internalError(p.config, n.info, "destructor turned out to be not trivial") discard "ignore calls to the default destructor" proc genDispose(p: BProc; n: PNode) = when false: - let elemType = n[1].typ.skipTypes(abstractVarInst).lastSon + let elemType = n[1].typ.skipTypes(abstractVar).lastSon var a: TLoc initLocExpr(p, n[1].skipAddr, a) @@ -2146,6 +2149,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mWasMoved: genWasMoved(p, e) of mMove: genMove(p, e, d) of mDestroy: genDestroy(p, e) + of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0") of mSlice: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & " 'toOpenArray' is only valid within a call expression") @@ -2541,7 +2545,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = initLocExprSingleUse(p, ex, a) line(p, cpsStmts, "(void)(" & a.r & ");\L") of nkAsmStmt: genAsmStmt(p, n) - of nkTryStmt: + of nkTryStmt, nkHiddenTryStmt: if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: genTryCpp(p, n, d) else: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index a7c1895f6..1436880d9 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -539,7 +539,7 @@ proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = include ccgcalls, "ccgstmts.nim" -proc initFrame(p: BProc, procname, filename: Rope): Rope = +proc initFrame(p: BProc, procname, filename: Rope): Rope = const frameDefines = """ $1 define nimfr_(proc, file) \ TFrame FR_; \ @@ -868,7 +868,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = of nkSym: # some path reads from 'result' before it was written to! if n.sym.kind == skResult: result = InitRequired - of nkTryStmt: + of nkTryStmt, nkHiddenTryStmt: # We need to watch out for the following problem: # try: # result = stuffThatRaises() diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 5ded6d054..6f6bad942 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -541,7 +541,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = if isExpr: result.add(ctx.newEnvVarAccess(tmp)) - of nkTryStmt: + of nkTryStmt, nkHiddenTryStmt: var ns = false for i in 0 ..< n.len: n[i] = ctx.lowerStmtListExprs(n[i], ns) @@ -934,7 +934,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut) result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut) - of nkTryStmt: + of nkTryStmt, nkHiddenTryStmt: # See explanation above about how this works ctx.hasExceptions = true diff --git a/compiler/commands.nim b/compiler/commands.nim index 2bccc4d81..3a18e69ed 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -286,7 +286,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool of "taintmode": result = contains(conf.globalOptions, optTaintMode) of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation) of "implicitstatic": result = contains(conf.options, optImplicitStatic) - of "patterns": result = contains(conf.options, optPatterns) + of "patterns", "trmacros": result = contains(conf.options, optTrMacros) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) of "nilseqs": result = contains(conf.options, optNilSeqs) of "oldast": result = contains(conf.options, optOldAst) @@ -532,8 +532,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info) of "implicitstatic": processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info) - of "patterns": - processOnOffSwitch(conf, {optPatterns}, arg, pass, info) + of "patterns", "trmacros": + processOnOffSwitch(conf, {optTrMacros}, arg, pass, info) of "opt": expectArg(conf, switch, arg, pass, info) case arg.normalize diff --git a/compiler/dfa.nim b/compiler/dfa.nim index ec69f381d..1a7c39417 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -665,7 +665,7 @@ proc gen(c: var Con; n: PNode) = of nkReturnStmt: genReturn(c, n) of nkRaiseStmt: genRaise(c, n) of nkBreakStmt: genBreak(c, n) - of nkTryStmt: genTry(c, n) + of nkTryStmt, nkHiddenTryStmt: genTry(c, n) of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange, nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr: for x in n: gen(c, x) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 2c7e52c67..93efef526 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -15,7 +15,7 @@ import ast, strutils, strtabs, options, msgs, os, ropes, idents, wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, packages/docutils/rst, packages/docutils/rstgen, - packages/docutils/highlite, sempass2, json, xmltree, cgi, + packages/docutils/highlite, json, xmltree, cgi, trees, types, typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets, pathutils @@ -760,6 +760,63 @@ proc exportSym(d: PDoc; s: PSym) = "$1", [rope esc(d.target, s.name.s), rope changeFileExt(external, "html")]) +proc documentNewEffect(cache: IdentCache; n: PNode): PNode = + let s = n.sons[namePos].sym + if tfReturnsNew in s.typ.flags: + result = newIdentNode(getIdent(cache, "new"), n.info) + +proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode = + let spec = effectSpec(x, effectType) + if isNil(spec): + let s = n.sons[namePos].sym + + let actual = s.typ.n.sons[0] + if actual.len != effectListLen: return + let real = actual.sons[idx] + + # warning: hack ahead: + var effects = newNodeI(nkBracket, n.info, real.len) + for i in 0 ..< real.len: + var t = typeToString(real[i].typ) + if t.startsWith("ref "): t = substr(t, 4) + effects.sons[i] = newIdentNode(getIdent(cache, t), n.info) + # set the type so that the following analysis doesn't screw up: + effects.sons[i].typ = real[i].typ + + result = newNode(nkExprColonExpr, n.info, @[ + newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects]) + +proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = + let s = n.sons[namePos].sym + let params = s.typ.n + + var effects = newNodeI(nkBracket, n.info) + for i in 1 ..< params.len: + if params[i].kind == nkSym and flag in params[i].sym.flags: + effects.add params[i] + + if effects.len > 0: + result = newNode(nkExprColonExpr, n.info, @[ + newIdentNode(getIdent(cache, pragmaName), n.info), effects]) + +proc documentRaises*(cache: IdentCache; n: PNode) = + if n.sons[namePos].kind != nkSym: return + let pragmas = n.sons[pragmasPos] + let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects) + let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects) + let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes") + let p4 = documentNewEffect(cache, n) + let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes") + + if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil: + if pragmas.kind == nkEmpty: + n.sons[pragmasPos] = newNodeI(nkPragma, n.info) + if p1 != nil: n.sons[pragmasPos].add p1 + if p2 != nil: n.sons[pragmasPos].add p2 + if p3 != nil: n.sons[pragmasPos].add p3 + if p4 != nil: n.sons[pragmasPos].add p4 + if p5 != nil: n.sons[pragmasPos].add p5 + proc generateDoc*(d: PDoc, n, orig: PNode) = case n.kind of nkCommentStmt: add(d.modDesc, genComment(d, n)) diff --git a/compiler/hlo.nim b/compiler/hlo.nim index bbbcb4e56..8ebc1ec35 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -92,12 +92,12 @@ proc hlo(c: PContext, n: PNode): PNode = proc hloBody(c: PContext, n: PNode): PNode = # fast exit: - if c.patterns.len == 0 or optPatterns notin c.config.options: return n + if c.patterns.len == 0 or optTrMacros notin c.config.options: return n c.hloLoopDetector = 0 result = hlo(c, n) proc hloStmt(c: PContext, n: PNode): PNode = # fast exit: - if c.patterns.len == 0 or optPatterns notin c.config.options: return n + if c.patterns.len == 0 or optTrMacros notin c.config.options: return n c.hloLoopDetector = 0 result = hlo(c, n) diff --git a/compiler/destroyer.nim b/compiler/injectdestructors.nim index 10fc37f32..e9323834e 100644 --- a/compiler/destroyer.nim +++ b/compiler/injectdestructors.nim @@ -91,7 +91,7 @@ ## destroy(tmp.x); destroy(tmp.y) ## -##[ +#[ From https://github.com/nim-lang/Nim/wiki/Destructors Rule Pattern Transformed into @@ -131,7 +131,7 @@ copyMem. This is harder than it looks: And the C++ optimizers don't sweat to optimize it for us, so we don't have to do it. -]## +]# import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, @@ -315,6 +315,10 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = if c.otherRead != nil: m.add "; another read is done here: " m.add c.graph.config $ c.otherRead.info + elif ri.kind == nkSym and ri.sym.kind == skParam and ri.sym.typ.kind != tySink: + m.add "; try to make " + m.add renderTree(ri) + m.add " a 'sink' parameter" localError(c.graph.config, ri.info, errGenerated, m) proc makePtrType(c: Con, baseType: PType): PType = @@ -329,7 +333,7 @@ template genOp(opr, opname, ri) = elif op.ast[genericParamsPos].kind != nkEmpty: globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic") - patchHead op + #patchHead op if sfError in op.flags: checkForErrorPragma(c, t, ri, opname) let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) addrExp.add(dest) @@ -343,7 +347,14 @@ proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = echo t.sink.id, " owner ", t.id quit 1 let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) - genOp(if t.sink != nil: t.sink else: t.assignment, "=sink", ri) + let op = if t.sink != nil: t.sink else: t.assignment + if op != nil: + genOp(op, "=sink", ri) + else: + # in rare cases only =destroy exists but no sink or assignment + # (see Pony object in tmove_objconstr.nim) + # we generate a fast assignment in this case: + result = newTree(nkFastAsgn, dest, ri) proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode = if tfHasOwned in t.flags: @@ -710,6 +721,7 @@ proc p(n: PNode; c: var Con): PNode = proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = when false: # defined(nimDebugDestroys): echo "injecting into ", n + if sfGeneratedOp in owner.flags: return n var c: Con c.owner = owner c.destroys = newNodeI(nkStmtList, n.info) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 627cc6cdf..b1a7d12f3 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2387,7 +2387,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genLineDir(p, n) gen(p, n.sons[0], r) of nkAsmStmt: genAsmOrEmitStmt(p, n) - of nkTryStmt: genTry(p, n, r) + of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 4179c8e5d..4ae438e48 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -10,7 +10,7 @@ ## This module implements lifting for type-bound operations ## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``). -# included from sem.nim +# included from sempass2.nim type TLiftCtx = object @@ -20,9 +20,10 @@ type fn: PSym asgnForType: PType recurse: bool + c: PContext proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) -proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; +proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym {.discardable.} proc at(a, i: PNode, elemType: PType): PNode = @@ -118,8 +119,18 @@ proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} = (tfHasGCedMem in t.flags or t.isGCedMem) proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; - field: PSym): bool = - if tfHasAsgn in t.flags or useNoGc(c, t): + field: var PSym): bool = + if optNimV2 in c.graph.config.globalOptions: + let op = field + if field != nil and sfOverriden in field.flags: + if sfError in op.flags: + incl c.fn.flags, sfError + else: + markUsed(c.graph.config, c.info, op, c.graph.usageSym) + onUse(c.info, op) + body.add newAsgnCall(c.graph, op, x, y) + result = true + elif tfHasAsgn in t.flags: var op: PSym if sameType(t, c.asgnForType): # generate recursive call: @@ -131,19 +142,29 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; else: op = field if op == nil: - op = liftBody(c.graph, t, c.kind, c.info) + op = liftBody(c.c, t, c.kind, c.info) if sfError in op.flags: incl c.fn.flags, sfError else: markUsed(c.graph.config, c.info, op, c.graph.usageSym) onUse(c.info, op) + # We also now do generic instantiations in the destructor lifting pass: + if op.ast[genericParamsPos].kind != nkEmpty: + assert t.typeInst != nil + op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1) + field = op + #echo "trying to use ", op.ast + #echo "for ", op.name.s, " " + #debug(t) + #return false + assert op.ast[genericParamsPos].kind == nkEmpty body.add newAsgnCall(c.graph, op, x, y) result = true proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool = var op = t.destructor if op == nil and useNoGc(c, t): - op = liftBody(c.graph, t, attachedDestructor, c.info) + op = liftBody(c.c, t, attachedDestructor, c.info) doAssert op != nil doAssert op == t.destructor @@ -159,7 +180,20 @@ proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool = proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = case c.kind of attachedDestructor: - result = addDestructorCall(c, t, body, x) + var op = t.destructor + if op != nil and sfOverriden in op.flags: + + if op.ast[genericParamsPos].kind != nkEmpty: + assert t.typeInst != nil + # patch generic destructor: + op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1) + t.destructor = op + + markUsed(c.graph.config, c.info, op, c.graph.usageSym) + onUse(c.info, op) + body.add destructorCall(c.graph, op, x) + result = true + #result = addDestructorCall(c, t, body, x) of attachedAsgn: result = considerAsgnOrSink(c, t, body, x, y, t.assignment) of attachedSink: @@ -221,10 +255,10 @@ proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode = lenCall.typ = getSysType(g, x.info, tyInt) result.add lenCall -proc setLenCall(g: ModuleGraph; x, y: PNode): PNode = +proc setLenCall(g: ModuleGraph; x, y: PNode; m: TMagic): PNode = let lenCall = genBuiltin(g, mLengthSeq, "len", y) lenCall.typ = getSysType(g, x.info, tyInt) - result = genBuiltin(g, mSetLengthSeq, "setLen", genAddr(g, x)) + result = genBuiltin(g, m, "setLen", genAddr(g, x)) result.add lenCall proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) = @@ -244,7 +278,7 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # var i = 0 # while i < y.len: dest[i] = y[i]; inc(i) # This is usually more efficient than a destroy/create pair. - body.add setLenCall(c.graph, x, y) + body.add setLenCall(c.graph, x, y, mSetLengthSeq) forallElements(c, t, body, x, y) of attachedSink: let moveCall = genBuiltin(c.graph, mMove, "move", x) @@ -273,7 +307,23 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, genVerbatim("dest@len != 0 && dest@region", c.info), deallocStmt) proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = - seqOp(c, t, body, x, y) + case c.kind + of attachedAsgn, attachedDeepCopy: + # we generate: + # setLen(dest, y.len) + # var i = 0 + # while i < y.len: dest[i] = y[i]; inc(i) + # This is usually more efficient than a destroy/create pair. + body.add setLenCall(c.graph, x, y, mSetLengthStr) + forallElements(c, t, body, x, y) + of attachedSink: + let moveCall = genBuiltin(c.graph, mMove, "move", x) + moveCall.add y + doAssert t.destructor != nil + moveCall.add destructorCall(c.graph, t.destructor, x) + body.add moveCall + of attachedDestructor: + body.add genBuiltin(c.graph, mDestroy, "destroy", x) proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -322,13 +372,34 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = call.sons[1] = y body.add newAsgnStmt(x, call) elif optNimV2 in c.graph.config.globalOptions: + let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x) case c.kind - of attachedSink, attachedAsgn: discard - of attachedDestructor: discard + of attachedSink: + # we 'nil' y out afterwards so we *need* to take over its reference + # count value: + body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx)) + body.add newAsgnStmt(x, y) + of attachedAsgn: + body.add callCodegenProc(c.graph, "nimIncWeakRef", c.info, y) + body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx)) + body.add newAsgnStmt(x, y) + of attachedDestructor: + body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx)) of attachedDeepCopy: assert(false, "cannot happen") proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = - discard "to implement" + let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x) + var actions = newNodeI(nkStmtList, c.info) + let elemType = t.lastSon + discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx)) + actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, xx) + case c.kind + of attachedSink, attachedAsgn: + body.add genIf(c, xx, actions) + body.add newAsgnStmt(x, y) + of attachedDestructor: + body.add genIf(c, xx, actions) + of attachedDeepCopy: assert(false, "cannot happen") proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind @@ -417,35 +488,35 @@ proc addParam(procType: PType; param: PSym) = addSon(procType.n, newSymNode(param)) rawAddSon(procType, param.typ) -proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = +proc liftBodyDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = assert typ.kind == tyDistinct let baseType = typ[0] case kind of attachedAsgn: if baseType.assignment == nil: - discard liftBody(g, baseType, kind, info) + discard liftBody(c, baseType, kind, info) typ.assignment = baseType.assignment result = typ.assignment of attachedSink: if baseType.sink == nil: - discard liftBody(g, baseType, kind, info) + discard liftBody(c, baseType, kind, info) typ.sink = baseType.sink result = typ.sink of attachedDeepCopy: if baseType.deepCopy == nil: - discard liftBody(g, baseType, kind, info) + discard liftBody(c, baseType, kind, info) typ.deepCopy = baseType.deepCopy result = typ.deepCopy of attachedDestructor: if baseType.destructor == nil: - discard liftBody(g, baseType, kind, info) + discard liftBody(c, baseType, kind, info) typ.destructor = baseType.destructor result = typ.destructor -proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; +proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = if typ.kind == tyDistinct: - return liftBodyDistinctType(g, typ, kind, info) + return liftBodyDistinctType(c, typ, kind, info) when false: var typ = typ if c.config.selectedGC == gcDestructors and typ.kind == tySequence: @@ -454,8 +525,10 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; var a: TLiftCtx a.info = info - a.graph = g + a.graph = c.graph a.kind = kind + a.c = c + let g = c.graph let body = newNodeI(nkStmtList, info) let procname = case kind of attachedAsgn: getIdent(g.cache, "=") @@ -477,26 +550,14 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; if kind != attachedDestructor: result.typ.addParam src - if optNimV2 in g.config.globalOptions: - case kind - of attachedAsgn: typ.assignment = result - of attachedSink: typ.sink = result - of attachedDeepCopy: typ.deepCopy = result - of attachedDestructor: typ.destructor = result + # register this operation already: + 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)) - if optNimV2 notin g.config.globalOptions: - # recursion is handled explicitly, do not register the type based operation - # before 'liftBodyAux': - if g.config.selectedGC == gcDestructors and - typ.kind in {tySequence, tyString} and body.len == 0: - discard "do not cache it yet" - else: - case kind - of attachedAsgn: typ.assignment = result - of attachedSink: typ.sink = result - of attachedDeepCopy: typ.deepCopy = result - of attachedDestructor: typ.destructor = result var n = newNodeI(nkProcDef, info, bodyPos+1) for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info) @@ -505,32 +566,66 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; n.sons[bodyPos] = body result.ast = n incl result.flags, sfFromGeneric + incl result.flags, sfGeneratedOp - -proc getAsgnOrLiftBody(g: ModuleGraph; typ: PType; info: TLineInfo): PSym = +proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) result = t.assignment if result.isNil: - result = liftBody(g, t, attachedAsgn, info) - -proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode = - let a = getAsgnOrLiftBody(g, dest.typ, dest.info) - result = newAsgnCall(g, a, dest, src) - -proc liftTypeBoundOps*(g: ModuleGraph; typ: PType; info: TLineInfo) = + 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.graph, a, dest, src) + +template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = + discard "now a nop" + +proc patchBody(c: PContext; n: PNode; info: TLineInfo) = + if n.kind in nkCallKinds: + if n[0].kind == nkSym and n[0].sym.magic == mDestroy: + let t = n[1].typ.skipTypes(abstractVar) + if t.destructor == nil: + liftBody(c, t, attachedDestructor, info) + + if t.destructor != nil: + if t.destructor.ast[genericParamsPos].kind != nkEmpty: + internalError(c.graph.config, info, "resolved destructor is generic") + if t.destructor.magic == mDestroy: + internalError(c.graph.config, info, "patching mDestroy with mDestroy?") + n.sons[0] = newSymNode(t.destructor) + for x in n: patchBody(c, x, info) + +template inst(field, t) = + if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty: + assert t.typeInst != nil + field = c.instTypeBoundOp(c, field, t.typeInst, info, attachedAsgn, 1) + if field.ast != nil: + patchBody(c, field.ast, info) + +proc createTypeBoundOps*(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. - ## The later 'destroyer' pass depends on it. - if not hasDestructor(typ): return - when false: - # do not produce wrong liftings while we're still instantiating generics: - # now disabled; breaks topttree.nim! - if c.typesWithOps.len > 0: return + ## The later 'injectdestructors' pass depends on it. + if typ == nil or {tfCheckedForDestructor, tfHasMeta} * typ.flags != {}: return + incl typ.flags, tfCheckedForDestructor + # multiple cases are to distinguish here: + # 1. we don't know yet if 'typ' has a nontrival destructor. + # 2. we have a nop destructor. --> mDestroy + # 3. we have a lifted destructor. + # 4. We have a custom destructor. + # 5. We have a (custom) generic destructor. let typ = typ.skipTypes({tyGenericInst, tyAlias}) # we generate the destructor first so that other operators can depend on it: if typ.destructor == nil: - liftBody(g, typ, attachedDestructor, info) + liftBody(c, typ, attachedDestructor, info) + else: + inst(typ.destructor, typ) if typ.assignment == nil: - liftBody(g, typ, attachedAsgn, info) + liftBody(c, typ, attachedAsgn, info) + else: + inst(typ.assignment, typ) if typ.sink == nil: - liftBody(g, typ, attachedSink, info) + liftBody(c, typ, attachedSink, info) + else: + inst(typ.sink, typ) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index b78bccceb..e1eff7a70 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -80,7 +80,7 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode = addSon(result, lit) proc newTryFinally*(body, final: PNode): PNode = - result = newTree(nkTryStmt, body, newTree(nkFinally, final)) + result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final)) proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode = let value = n.lastSon diff --git a/compiler/options.nim b/compiler/options.nim index cf89e3488..2ab5309bf 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -36,7 +36,7 @@ type # please make sure we have under 32 options optProfiler, # profiler turned on optImplicitStatic, # optimization: implicit at compile time # evaluation - optPatterns, # en/disable pattern matching + optTrMacros, # en/disable pattern matching optMemTracker, optLaxStrings, optNilSeqs, @@ -273,7 +273,7 @@ const DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optHints, optStackTrace, optLineTrace, - optPatterns, optNilCheck, optMoveCheck} + optTrMacros, optNilCheck, optMoveCheck} DefaultGlobalOptions* = {optThreadAnalysis} proc getSrcTimestamp(): DateTime = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 4ef662f50..8a701d7f3 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -48,7 +48,7 @@ const wDeadCodeElimUnused, # deprecated, always on wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, - wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto, + wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, wInjectStmt, wDeprecated, wExperimental, wThis} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, @@ -350,7 +350,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = of wMemTracker: {optMemTracker} of wByRef: {optByRef} of wImplicitStatic: {optImplicitStatic} - of wPatterns: {optPatterns} + of wPatterns, wTrMacros: {optTrMacros} else: {} proc processExperimental(c: PContext; n: PNode) = @@ -1011,7 +1011,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler, - wFloatchecks, wNanChecks, wInfChecks, wPatterns: + wFloatchecks, wNanChecks, wInfChecks, wPatterns, wTrMacros: processOption(c, it, c.config.options) of wStacktrace, wLinetrace: if sym.kind in {skProc, skMethod, skConverter}: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 832add378..5d65cf6d3 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -503,7 +503,7 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkReturnStmt: if n.len > 0 and n[0].kind == nkAsgn: result = len("return_") + lsub(g, n[0][1]) - else: + else: result = len("return_") + lsub(g, n[0]) of nkRaiseStmt: result = lsub(g, n.sons[0]) + len("raise_") of nkYieldStmt: result = lsub(g, n.sons[0]) + len("yield_") @@ -1295,7 +1295,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkWhileStmt: gwhile(g, n) of nkPragmaBlock: gpragmaBlock(g, n) of nkCaseStmt, nkRecCase: gcase(g, n) - of nkTryStmt: gtry(g, n) + of nkTryStmt, nkHiddenTryStmt: gtry(g, n) of nkForStmt, nkParForStmt: gfor(g, n) of nkBlockStmt, nkBlockExpr: gblock(g, n) of nkStaticStmt: gstaticStmt(g, n) @@ -1345,9 +1345,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) of nkReturnStmt: putWithSpace(g, tkReturn, "return") - if n.len > 0 and n[0].kind == nkAsgn: + if n.len > 0 and n[0].kind == nkAsgn: gsub(g, n[0], 1) - else: + else: gsub(g, n, 0) of nkRaiseStmt: putWithSpace(g, tkRaise, "raise") diff --git a/compiler/sem.nim b/compiler/sem.nim index 0a3b60ab3..66d963e16 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -586,7 +586,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = result = buildEchoStmt(c, result) if c.config.cmd == cmdIdeTools: appendToModule(c.module, result) - trackTopLevelStmt(c.graph, c.module, result) + trackTopLevelStmt(c, c.module, result) proc recoverContext(c: PContext) = # clean up in case of a semantic error: We clean up the stacks, etc. This is diff --git a/compiler/semdata.nim b/compiler/semdata.nim index a05bda32d..bfe7d5f33 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -298,10 +298,12 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = result = typ else: result = newTypeS(tyTypeDesc, c) + incl result.flags, tfCheckedForDestructor result.addSonSkipIntLit(typ) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = newTypeS(tyTypeDesc, c) + incl typedesc.flags, tfCheckedForDestructor typedesc.addSonSkipIntLit(assertNotNil(c.config, typ)) let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, c.config.options).linkTo(typedesc) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3e8edc687..5cc2e96a6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -815,7 +815,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = result = magicsAfterOverloadResolution(c, result, flags) if result.typ != nil and not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty): - liftTypeBoundOps(c.graph, result.typ, n.info) + liftTypeBoundOps(c, result.typ, n.info) #result = patchResolvedTypeBoundOp(c, result) if c.matchedConcept == nil: result = evalAtCompileTime(c, result) @@ -1616,7 +1616,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = typeMismatch(c.config, n.info, lhs.typ, rhsTyp) n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) - liftTypeBoundOps(c.graph, lhs.typ, lhs.info) + liftTypeBoundOps(c, lhs.typ, lhs.info) #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info) fixAbstractType(c, n) @@ -2617,7 +2617,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkTypeSection: result = semTypeSection(c, n) of nkDiscardStmt: result = semDiscard(c, n) of nkWhileStmt: result = semWhile(c, n, flags) - of nkTryStmt: result = semTry(c, n, flags) + of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n, flags) of nkCaseStmt: result = semCase(c, n, flags) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 2810fca9e..0ba6c302f 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -369,7 +369,7 @@ proc semGenericStmt(c: PContext, n: PNode, addTempDecl(c, n.sons[0], skLabel) n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) closeScope(c) - of nkTryStmt: + of nkTryStmt, nkHiddenTryStmt: checkMinSonsLen(n, 2, c.config) n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx) for i in countup(1, sonsLen(n)-1): diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 51303d1b5..2423a428b 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -147,7 +147,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = freshGenSyms(b, result, orig, symMap) b = semProcBody(c, b) result.ast[bodyPos] = hloBody(c, b) - trackProc(c.graph, result, result.ast[bodyPos]) + trackProc(c, result, result.ast[bodyPos]) excl(result.flags, sfForward) dec c.inGenericInst diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index a0c35c9ca..f290a08d5 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -428,4 +428,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}: localError(c.config, n.info, "finalizer must be a direct reference to a procedure") result = n + of mDestroy: + result = n + let t = n[1].typ.skipTypes(abstractVar) + if t.destructor != nil: + result.sons[0] = newSymNode(t.destructor) else: result = n diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 137c5d336..a58111955 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -352,7 +352,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) = if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple: c.addSlice(n, n[0], n[1], n[1]) analyseSons(c, n) - of nkReturnStmt, nkRaiseStmt, nkTryStmt: + of nkReturnStmt, nkRaiseStmt, nkTryStmt, nkHiddenTryStmt: localError(c.graph.config, n.info, "invalid control flow for 'parallel'") # 'break' that leaves the 'parallel' section is not valid either # or maybe we should generate a 'try' XXX diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 6ac90f617..a3a474828 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -9,8 +9,8 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - wordrecg, strutils, options, guards, lineinfos, semfold, - modulegraphs + wordrecg, strutils, options, guards, lineinfos, semfold, semdata, + modulegraphs, lowerings, sigmatch when not defined(leanCompiler): import writetracking @@ -18,11 +18,30 @@ when not defined(leanCompiler): when defined(useDfa): import dfa -# Second semantic checking pass over the AST. Necessary because the old -# way had some inherent problems. Performs: -# -# * effect+exception tracking -# * "usage before definition" checking +include liftdestructors + +#[ Second semantic checking pass over the AST. Necessary because the old + way had some inherent problems. Performs: + +* effect+exception tracking +* "usage before definition" checking +* also now calls the "lift destructor logic" at strategic positions, this + is about to be put into the spec: + +We treat assignment and sinks and destruction as identical. + +In the construct let/var x = expr() x's type is marked. + +In x = y the type of x is marked. + +For every sink parameter of type T T is marked. TODO! + +For every call f() the return type of f() is marked. + + + + +]# # ------------------------ exception and tag tracking ------------------------- @@ -60,6 +79,7 @@ type maxLockLevel, currLockLevel: TLockLevel config: ConfigRef graph: ModuleGraph + c: PContext PEffects = var TEffects proc `<`(a, b: TLockLevel): bool {.borrow.} @@ -416,73 +436,6 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) = # list the computed effects up to here: listEffects(tracked) -proc effectSpec(n: PNode, effectType: TSpecialWord): PNode = - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - if it.kind == nkExprColonExpr and whichPragma(it) == effectType: - result = it.sons[1] - if result.kind notin {nkCurly, nkBracket}: - result = newNodeI(nkCurly, result.info) - result.add(it.sons[1]) - return - -proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode = - let spec = effectSpec(x, effectType) - if isNil(spec): - let s = n.sons[namePos].sym - - let actual = s.typ.n.sons[0] - if actual.len != effectListLen: return - let real = actual.sons[idx] - - # warning: hack ahead: - var effects = newNodeI(nkBracket, n.info, real.len) - for i in 0 ..< real.len: - var t = typeToString(real[i].typ) - if t.startsWith("ref "): t = substr(t, 4) - effects.sons[i] = newIdentNode(getIdent(cache, t), n.info) - # set the type so that the following analysis doesn't screw up: - effects.sons[i].typ = real[i].typ - - result = newNode(nkExprColonExpr, n.info, @[ - newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects]) - -proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = - let s = n.sons[namePos].sym - let params = s.typ.n - - var effects = newNodeI(nkBracket, n.info) - for i in 1 ..< params.len: - if params[i].kind == nkSym and flag in params[i].sym.flags: - effects.add params[i] - - if effects.len > 0: - result = newNode(nkExprColonExpr, n.info, @[ - newIdentNode(getIdent(cache, pragmaName), n.info), effects]) - -proc documentNewEffect(cache: IdentCache; n: PNode): PNode = - let s = n.sons[namePos].sym - if tfReturnsNew in s.typ.flags: - result = newIdentNode(getIdent(cache, "new"), n.info) - -proc documentRaises*(cache: IdentCache; n: PNode) = - if n.sons[namePos].kind != nkSym: return - let pragmas = n.sons[pragmasPos] - let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects) - let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects) - let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes") - let p4 = documentNewEffect(cache, n) - let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes") - - if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil: - if pragmas.kind == nkEmpty: - n.sons[pragmasPos] = newNodeI(nkPragma, n.info) - if p1 != nil: n.sons[pragmasPos].add p1 - if p2 != nil: n.sons[pragmasPos].add p2 - if p3 != nil: n.sons[pragmasPos].add p3 - if p4 != nil: n.sons[pragmasPos].add p4 - if p5 != nil: n.sons[pragmasPos].add p5 - template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {} proc importedFromC(n: PNode): bool = @@ -749,11 +702,13 @@ proc track(tracked: PEffects, n: PNode) = # Here we add a `Exception` tag in order to cover both the cases. addEffect(tracked, createRaise(tracked.graph, n)) of nkCallKinds: - if getConstExpr(tracked.owner_module, n, tracked.graph) != nil: - return # p's effects are ours too: var a = n.sons[0] let op = a.typ + if getConstExpr(tracked.owner_module, n, tracked.graph) != nil: + return + if op != nil: + createTypeBoundOps(tracked.c, op, n.info) if a.kind == nkCast and a[1].typ.kind == tyProc: a = a[1] # XXX: in rare situations, templates and macros will reach here after @@ -813,10 +768,12 @@ proc track(tracked: PEffects, n: PNode) = addAsgnFact(tracked.guards, n.sons[0], n.sons[1]) notNilCheck(tracked, n.sons[1], n.sons[0].typ) when false: cstringCheck(tracked, n) + createTypeBoundOps(tracked.c, n[0].typ, n.info) of nkVarSection, nkLetSection: for child in n: let last = lastSon(child) if last.kind != nkEmpty: track(tracked, last) + createTypeBoundOps(tracked.c, child[0].typ, child.info) if child.kind == nkIdentDefs and last.kind != nkEmpty: for i in 0 .. child.len-3: initVar(tracked, child.sons[i], volatileCheck=false) @@ -965,7 +922,7 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = effects[tagEffects] = tagsSpec effects[pragmasEffects] = n -proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) = +proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info) @@ -983,8 +940,10 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) = t.locked = @[] t.graph = g t.config = g.config + t.c = c -proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) = +proc trackProc*(c: PContext; s: PSym, body: PNode) = + let g = c.graph var effects = s.typ.n.sons[0] if effects.kind != nkEffectList: return # effects already computed? @@ -992,7 +951,7 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) = if effects.len == effectListLen: return var t: TEffects - initEffects(g, effects, s, t) + initEffects(g, effects, s, t, c) track(t, body) if not isEmptyType(s.typ.sons[0]) and {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and @@ -1043,12 +1002,13 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) = dataflowAnalysis(s, body) when false: trackWrites(s, body) -proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) = +proc trackTopLevelStmt*(c: PContext; module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef, nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}: return + let g = c.graph var effects = newNode(nkEffectList, n.info) var t: TEffects - initEffects(g, effects, module, t) + initEffects(g, effects, module, t, c) t.isToplevel = true track(t, n) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c3d909824..bc403f1ea 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -339,7 +339,7 @@ proc checkNilable(c: PContext; v: PSym) = elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags: message(c.config, v.info, warnProveInit, v.name.s) -include liftdestructors +#include liftdestructors proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) = let L = identDefs.len @@ -484,7 +484,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # this can only happen for errornous var statements: if typ == nil: continue typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {}) - liftTypeBoundOps(c.graph, typ, a.info) + liftTypeBoundOps(c, typ, a.info) instAllTypeBoundOp(c, a.info) var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) if a.kind == nkVarTuple: @@ -1265,7 +1265,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = checkConstructedType(c.config, s.info, s.typ) if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: checkForMetaFields(c, s.typ.n) - instAllTypeBoundOp(c, n.info) + #instAllTypeBoundOp(c, n.info) proc semAllTypeSections(c: PContext; n: PNode): PNode = @@ -1474,7 +1474,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = addResult(c, s.typ.sons[0], n.info, skProc) addResultNode(c, n) s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos])) - trackProc(c.graph, s, s.ast[bodyPos]) + trackProc(c, s, s.ast[bodyPos]) popProcCon(c) elif efOperand notin flags: localError(c.config, n.info, errGenericLambdaNotAllowed) @@ -1515,7 +1515,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = addResult(c, n.typ.sons[0], n.info, skProc) addResultNode(c, n) s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos])) - trackProc(c.graph, s, s.ast[bodyPos]) + trackProc(c, s, s.ast[bodyPos]) popProcCon(c) popOwner(c) closeScope(c) @@ -1552,6 +1552,14 @@ proc canonType(c: PContext, t: PType): PType = result = t proc semOverride(c: PContext, s: PSym, n: PNode) = + proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) = + var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj) + if sfOverriden notin prevOp.flags: + msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info) + else: + msg.add "; previous declaration was here: " & (c.config $ prevOp.info) + localError(c.config, n.info, errGenerated, msg) + let name = s.name.s.normalize case name of "=destroy": @@ -1569,8 +1577,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if obj.destructor.isNil: obj.destructor = s else: - localError(c.config, n.info, errGenerated, - "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) + prevDestructor(c, obj.destructor, obj, n.info) noError = true if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, @@ -1601,8 +1608,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = "cannot bind 'deepCopy' to: " & typeToString(t)) if t.owner.getModule != s.getModule: - localError(c.config, n.info, errGenerated, - "type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")") + localError(c.config, n.info, errGenerated, + "type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")") else: localError(c.config, n.info, errGenerated, @@ -1635,11 +1642,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if opr[].isNil: opr[] = s else: - localError(c.config, n.info, errGenerated, - "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) + prevDestructor(c, opr[], obj, n.info) if obj.owner.getModule != s.getModule: - localError(c.config, n.info, errGenerated, - "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") + localError(c.config, n.info, errGenerated, + "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") return if sfSystemModule notin s.owner.flags: @@ -1862,7 +1868,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos])) # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': - trackProc(c.graph, s, s.ast[bodyPos]) + trackProc(c, s, s.ast[bodyPos]) else: if s.typ.sons[0] != nil and kind != skIterator: addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info)) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index dae2833ec..cbb7a95c9 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -391,7 +391,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = n.sons[0] = newSymNode(s, n.sons[0].info) n.sons[1] = semTemplBody(c, n.sons[1]) closeScope(c) - of nkTryStmt: + of nkTryStmt, nkHiddenTryStmt: checkMinSonsLen(n, 2, c.c.config) n.sons[0] = semTemplBodyScope(c, n.sons[0]) for i in countup(1, sonsLen(n)-1): diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6996082c3..f215d4a45 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -53,6 +53,7 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = proc newConstraint(c: PContext, k: TTypeKind): PType = result = newTypeS(tyBuiltInTypeClass, c) + result.flags.incl tfCheckedForDestructor result.addSonSkipIntLit(newTypeS(k, c)) proc semEnum(c: PContext, n: PNode, prev: PType): PType = @@ -951,8 +952,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramTypId.id == getIdent(c.cache, "typedesc").id: # XXX Why doesn't this check for tyTypeDesc instead? paramTypId = nil - result = addImplicitGeneric( - c.newTypeWithSons(tyTypeDesc, @[paramType.base])) + let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base]) + incl t.flags, tfCheckedForDestructor + result = addImplicitGeneric(t) of tyDistinct: if paramType.sonsLen == 1: @@ -1138,6 +1140,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # surprising behavior. We must instead fix the expected type of # the proc to be the unbound typedesc type: typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) + typ.flags.incl tfCheckedForDestructor else: # if def.typ != nil and def.typ.kind != tyNone: @@ -1411,6 +1414,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = inherited = n[2] result = newOrPrevType(tyUserTypeClass, prev, c) + result.flags.incl tfCheckedForDestructor var owner = getCurrOwner(c) var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType]) result.sons = @[candidateTypeSlot] @@ -1432,7 +1436,9 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = if modifier != tyNone: dummyName = param[0] dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot) - if modifier == tyTypeDesc: dummyType.flags.incl tfConceptMatchedTypeSym + if modifier == tyTypeDesc: + dummyType.flags.incl tfConceptMatchedTypeSym + dummyType.flags.incl tfCheckedForDestructor else: dummyName = param dummyType = candidateTypeSlot @@ -1752,7 +1758,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyError, prev, c) n.typ = result dec c.inTypeContext - if c.inTypeContext == 0: + if false: # c.inTypeContext == 0: #if $n == "var seq[StackTraceEntry]": # echo "begin ", n instAllTypeBoundOp(c, n.info) @@ -1779,24 +1785,28 @@ proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) = else: discard +proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) = + setMagicType(conf, m, kind, size) + incl m.typ.flags, tfCheckedForDestructor + proc processMagicType(c: PContext, m: PSym) = case m.magic - of mInt: setMagicType(c.config, m, tyInt, c.config.target.intSize) - of mInt8: setMagicType(c.config, m, tyInt8, 1) - of mInt16: setMagicType(c.config, m, tyInt16, 2) - of mInt32: setMagicType(c.config, m, tyInt32, 4) - of mInt64: setMagicType(c.config, m, tyInt64, 8) - of mUInt: setMagicType(c.config, m, tyUInt, c.config.target.intSize) - of mUInt8: setMagicType(c.config, m, tyUInt8, 1) - of mUInt16: setMagicType(c.config, m, tyUInt16, 2) - of mUInt32: setMagicType(c.config, m, tyUInt32, 4) - of mUInt64: setMagicType(c.config, m, tyUInt64, 8) - of mFloat: setMagicType(c.config, m, tyFloat, c.config.target.floatSize) - of mFloat32: setMagicType(c.config, m, tyFloat32, 4) - of mFloat64: setMagicType(c.config, m, tyFloat64, 8) - of mFloat128: setMagicType(c.config, m, tyFloat128, 16) - of mBool: setMagicType(c.config, m, tyBool, 1) - of mChar: setMagicType(c.config, m, tyChar, 1) + of mInt: setMagicIntegral(c.config, m, tyInt, c.config.target.intSize) + of mInt8: setMagicIntegral(c.config, m, tyInt8, 1) + of mInt16: setMagicIntegral(c.config, m, tyInt16, 2) + of mInt32: setMagicIntegral(c.config, m, tyInt32, 4) + of mInt64: setMagicIntegral(c.config, m, tyInt64, 8) + of mUInt: setMagicIntegral(c.config, m, tyUInt, c.config.target.intSize) + of mUInt8: setMagicIntegral(c.config, m, tyUInt8, 1) + of mUInt16: setMagicIntegral(c.config, m, tyUInt16, 2) + of mUInt32: setMagicIntegral(c.config, m, tyUInt32, 4) + of mUInt64: setMagicIntegral(c.config, m, tyUInt64, 8) + of mFloat: setMagicIntegral(c.config, m, tyFloat, c.config.target.floatSize) + of mFloat32: setMagicIntegral(c.config, m, tyFloat32, 4) + of mFloat64: setMagicIntegral(c.config, m, tyFloat64, 8) + of mFloat128: setMagicIntegral(c.config, m, tyFloat128, 16) + of mBool: setMagicIntegral(c.config, m, tyBool, 1) + of mChar: setMagicIntegral(c.config, m, tyChar, 1) of mString: setMagicType(c.config, m, tyString, szUncomputedSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) @@ -1804,29 +1814,29 @@ proc processMagicType(c: PContext, m: PSym) = if c.config.selectedGc == gcDestructors: incl m.typ.flags, tfHasAsgn of mCstring: - setMagicType(c.config, m, tyCString, c.config.target.ptrSize) + setMagicIntegral(c.config, m, tyCString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) - of mPointer: setMagicType(c.config, m, tyPointer, c.config.target.ptrSize) + of mPointer: setMagicIntegral(c.config, m, tyPointer, c.config.target.ptrSize) of mEmptySet: - setMagicType(c.config, m, tySet, 1) + setMagicIntegral(c.config, m, tySet, 1) rawAddSon(m.typ, newTypeS(tyEmpty, c)) - of mIntSetBaseType: setMagicType(c.config, m, tyRange, c.config.target.intSize) + of mIntSetBaseType: setMagicIntegral(c.config, m, tyRange, c.config.target.intSize) of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize) of mExpr: if m.name.s == "auto": - setMagicType(c.config, m, tyAnything, 0) + setMagicIntegral(c.config, m, tyAnything, 0) else: - setMagicType(c.config, m, tyExpr, 0) + setMagicIntegral(c.config, m, tyExpr, 0) of mStmt: - setMagicType(c.config, m, tyStmt, 0) + setMagicIntegral(c.config, m, tyStmt, 0) of mTypeDesc, mType: - setMagicType(c.config, m, tyTypeDesc, 0) + setMagicIntegral(c.config, m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mStatic: setMagicType(c.config, m, tyStatic, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: - setMagicType(c.config, m, tyVoid, 0) + setMagicIntegral(c.config, m, tyVoid, 0) of mArray: setMagicType(c.config, m, tyArray, szUncomputedSize) of mOpenArray: @@ -1834,12 +1844,12 @@ proc processMagicType(c: PContext, m: PSym) = of mVarargs: setMagicType(c.config, m, tyVarargs, szUncomputedSize) of mRange: - setMagicType(c.config, m, tyRange, szUncomputedSize) + setMagicIntegral(c.config, m, tyRange, szUncomputedSize) rawAddSon(m.typ, newTypeS(tyNone, c)) of mSet: - setMagicType(c.config, m, tySet, szUncomputedSize) + setMagicIntegral(c.config, m, tySet, szUncomputedSize) of mUncheckedArray: - setMagicType(c.config, m, tyUncheckedArray, szUncomputedSize) + setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize) of mSeq: setMagicType(c.config, m, tySequence, szUncomputedSize) if c.config.selectedGc == gcDestructors: @@ -1849,10 +1859,11 @@ proc processMagicType(c: PContext, m: PSym) = of mOpt: setMagicType(c.config, m, tyOpt, szUncomputedSize) of mOrdinal: - setMagicType(c.config, m, tyOrdinal, szUncomputedSize) + setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize) rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: incl m.typ.flags, tfTriggersCompileTime + incl m.typ.flags, tfCheckedForDestructor of mException: discard of mBuiltinType: case m.name.s @@ -1886,6 +1897,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if typ.kind == tyTypeDesc: if typ.sons[0].kind == tyNone: typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) + incl typ.flags, tfCheckedForDestructor else: typ = semGenericConstraints(c, typ) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index dcb6ea5a4..931a54f96 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -291,7 +291,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = when false: if newDestructors: result.assignment = nil - #result.destructor = nil + result.destructor = nil result.sink = nil proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = @@ -404,7 +404,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # adding myseq for myseq[system.int] # sigmatch: Formal myseq[=destroy.T] real myseq[system.int] #echo "DESTROY: adding ", typeToString(newbody), " for ", typeToString(result, preferDesc) - cl.c.typesWithOps.add((newbody, result)) + #cl.c.typesWithOps.add((newbody, result)) let mm = skipTypes(bbody, abstractPtrs) if tfFromGeneric notin mm.flags: # bug #5479, prevent endless recursions here: diff --git a/compiler/transf.nim b/compiler/transf.nim index cac0ded90..640ed1136 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -21,7 +21,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups, idents, renderer, types, passes, semfold, magicsys, cgmeth, - sempass2, lowerings, destroyer, liftlocals, + sempass2, lowerings, injectdestructors, liftlocals, modulegraphs, lineinfos proc transformBody*(g: ModuleGraph, prc: PSym, cache = true; diff --git a/compiler/trees.nim b/compiler/trees.nim index ca2360e12..55a3c619e 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -120,6 +120,16 @@ proc whichPragma*(n: PNode): TSpecialWord = let key = if n.kind in nkPragmaCallKinds and n.len > 0: n.sons[0] else: n if key.kind == nkIdent: result = whichKeyword(key.ident) +proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode = + for i in countup(0, sonsLen(n) - 1): + var it = n.sons[i] + if it.kind == nkExprColonExpr and whichPragma(it) == effectType: + result = it.sons[1] + if result.kind notin {nkCurly, nkBracket}: + result = newNodeI(nkCurly, result.info) + result.add(it.sons[1]) + return + proc unnestStmts(n, result: PNode) = if n.kind == nkStmtList: for x in items(n): unnestStmts(x, result) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 47b2c12e5..93df4081f 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -2031,7 +2031,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkBreakStmt: unused(c, n, dest) genBreak(c, n) - of nkTryStmt: genTry(c, n, dest) + of nkTryStmt, nkHiddenTryStmt: genTry(c, n, dest) of nkStmtList: #unused(c, n, dest) # XXX Fix this bug properly, lexim triggers it diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 56b8f1806..4da19779b 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -54,7 +54,7 @@ type wBoundchecks, wOverflowchecks, wNilchecks, wFloatchecks, wNanChecks, wInfChecks, wMoveChecks, wNonReloadable, wExecuteOnReload, - wAssertions, wPatterns, wWarnings, + wAssertions, wPatterns, wTrMacros, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, wDeadCodeElimUnused, # deprecated, dead code elim always happens wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, @@ -145,7 +145,7 @@ const "floatchecks", "nanchecks", "infchecks", "movechecks", "nonreloadable", "executeonreload", - "assertions", "patterns", "warnings", "hints", + "assertions", "patterns", "trmacros", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", "deadcodeelim", # deprecated, dead code elim always happens "safecode", "package", "noforward", "reorder", "norewrite", diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index 1ea1deb2d..04d3b7a16 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -122,7 +122,7 @@ proc returnsNewExpr*(n: PNode): NewLocation = nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast: result = returnsNewExpr(n.lastSon) of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, - nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt: + nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt, nkHiddenTryStmt: result = newLit for i in ord(n.kind == nkObjConstr) ..< n.len: let x = returnsNewExpr(n.sons[i]) diff --git a/doc/advopt.txt b/doc/advopt.txt index 46b9a12a6..3b5827aed 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -78,7 +78,7 @@ Advanced options: --tlsEmulation:on|off turn thread local storage emulation on|off --taintMode:on|off turn taint mode on|off --implicitStatic:on|off turn implicit compile time evaluation on|off - --patterns:on|off turn term rewriting macros on|off + --trmacros:on|off turn term rewriting macros on|off --multimethods:on|off turn multi-methods on|off --memTracker:on|off turn memory tracker on|off --hotCodeReloading:on|off diff --git a/lib/core/macros.nim b/lib/core/macros.nim index e48df38f5..85eb597b2 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -78,7 +78,7 @@ type nnkEnumTy, nnkEnumFieldDef, nnkArglist, nnkPattern - nnkReturnToken, + nnkHiddenTryStmt, nnkClosure, nnkGotoState, nnkState, diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim index 305a99fd3..6463a5a27 100644 --- a/lib/core/runtime_v2.nim +++ b/lib/core/runtime_v2.nim @@ -1,5 +1,5 @@ #[ -In this new runtime we simply the object layouts a bit: The runtime type +In this new runtime we simplify the object layouts a bit: The runtime type information is only accessed for the objects that have it and it's always at offset 0 then. The ``ref`` object header is independent from the runtime type and only contains a reference count. @@ -48,9 +48,18 @@ template `-!`(p: pointer, s: int): pointer = template head(p: pointer): ptr RefHeader = cast[ptr RefHeader](cast[int](p) -% sizeof(RefHeader)) +var allocs*: int + proc nimNewObj(size: int): pointer {.compilerRtl.} = - result = alloc0(size + sizeof(RefHeader)) +! sizeof(RefHeader) - # XXX Respect defined(useMalloc) here! + let s = size + sizeof(RefHeader) + when defined(nimscript): + discard + elif defined(useMalloc): + result = c_malloc(s) +! sizeof(RefHeader) + nimZeroMem(result, s) + else: + result = alloc0(s) +! sizeof(RefHeader) + inc allocs proc nimDecWeakRef(p: pointer) {.compilerRtl.} = dec head(p).rc @@ -59,10 +68,19 @@ proc nimIncWeakRef(p: pointer) {.compilerRtl.} = inc head(p).rc proc nimRawDispose(p: pointer) {.compilerRtl.} = - if head(p).rc != 0: - cstderr.rawWrite "[FATAL] dangling references exist\n" - quit 1 - dealloc(p -! sizeof(RefHeader)) + when not defined(nimscript): + if head(p).rc != 0: + cstderr.rawWrite "[FATAL] dangling references exist\n" + quit 1 + when defined(useMalloc): + c_free(p -! sizeof(RefHeader)) + else: + dealloc(p -! sizeof(RefHeader)) + if allocs > 0: + dec allocs + else: + cstderr.rawWrite "[FATAL] unpaired dealloc\n" + quit 1 proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} = let d = cast[ptr PNimType](p)[].destructor diff --git a/lib/system.nim b/lib/system.nim index 016675d55..88521a026 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2950,7 +2950,7 @@ when not defined(js) and not defined(nimscript): when not declared(sysFatal): include "system/fatal" -when defined(nimV2) and not defined(nimscript): +when defined(nimV2): include core/runtime_v2 import system/assertions diff --git a/tests/destructor/tinvalid_rebind.nim b/tests/destructor/tinvalid_rebind.nim new file mode 100644 index 000000000..0f15c8f9e --- /dev/null +++ b/tests/destructor/tinvalid_rebind.nim @@ -0,0 +1,15 @@ +discard """ +joinable: false +cmd: "nim check $file" +errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)" +line: 14 +""" + +type + Foo[T] = object + +proc main = + var f: Foo[int] + +proc `=destroy`[T](f: var Foo[T]) = + discard diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim index 90ac6fa27..fa5495689 100644 --- a/tests/destructor/topttree.nim +++ b/tests/destructor/topttree.nim @@ -22,6 +22,7 @@ var proc `=destroy`*[T](x: var opt[T]) = if x.data != nil: + mixin `=destroy` when not supportsCopyMem(T): `=destroy`(x.data[]) dealloc(x.data) |