more destructor based changes (#10885)
* mark user defined destructors with sfOverriden to simplify the logic * refactoring in preparation to merge liftings and generic instantiations for destructors * ast: introduce nkHiddenTryStmt for destructor generation in order to be able to optimize better the code later on * renamed 'patterns' switch to 'trmacros' as it was totally misleading before * destructors: introduce tfCheckedForDestructor flag in preparation of strict =destroy checking * test for invalid/too late destructor introductions * liftdestructors: make code robust for nimsuggest * --newruntime works for hello world again * newruntime: code generation for closures
This commit is contained in:
parent
25649616ea
commit
bf592c4e94
39 changed files with 443 additions and 242 deletions
|
|
@ -216,7 +216,7 @@ type
|
||||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
nkEnumFieldDef, # `ident = expr` in an enumeration
|
||||||
nkArgList, # argument list
|
nkArgList, # argument list
|
||||||
nkPattern, # a special pattern; used for matching
|
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)
|
nkClosure, # (prc, env)-pair (internally used for code gen)
|
||||||
nkGotoState, # used for the state machine (for iterators)
|
nkGotoState, # used for the state machine (for iterators)
|
||||||
nkState, # give a label to a code section (for iterators)
|
nkState, # give a label to a code section (for iterators)
|
||||||
|
|
@ -227,7 +227,7 @@ type
|
||||||
TNodeKinds* = set[TNodeKind]
|
TNodeKinds* = set[TNodeKind]
|
||||||
|
|
||||||
type
|
type
|
||||||
TSymFlag* = enum # already 34 flags!
|
TSymFlag* = enum # already 36 flags!
|
||||||
sfUsed, # read access of sym (for warnings) or simply used
|
sfUsed, # read access of sym (for warnings) or simply used
|
||||||
sfExported, # symbol is exported from module
|
sfExported, # symbol is exported from module
|
||||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
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
|
sfGenSym # symbol is 'gensym'ed; do not add to symbol table
|
||||||
sfNonReloadable # symbol will be left as-is when hot code reloading is on -
|
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
|
# 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]
|
TSymFlags* = set[TSymFlag]
|
||||||
|
|
||||||
|
|
@ -478,7 +480,7 @@ type
|
||||||
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
||||||
|
|
||||||
TNodeFlags* = set[TNodeFlag]
|
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
|
tfVarargs, # procedure has C styled varargs
|
||||||
# tyArray type represeting a varargs list
|
# tyArray type represeting a varargs list
|
||||||
tfNoSideEffect, # procedure type does not allow side effects
|
tfNoSideEffect, # procedure type does not allow side effects
|
||||||
|
|
@ -535,6 +537,8 @@ type
|
||||||
tfCovariant # covariant generic param mimicing a ptr type
|
tfCovariant # covariant generic param mimicing a ptr type
|
||||||
tfWeakCovariant # covariant generic param mimicing a seq/array type
|
tfWeakCovariant # covariant generic param mimicing a seq/array type
|
||||||
tfContravariant # contravariant generic param
|
tfContravariant # contravariant generic param
|
||||||
|
tfCheckedForDestructor # type was checked for having a destructor.
|
||||||
|
# If it has one, t.destructor is not nil.
|
||||||
|
|
||||||
TTypeFlags* = set[TTypeFlag]
|
TTypeFlags* = set[TTypeFlag]
|
||||||
|
|
||||||
|
|
@ -640,7 +644,7 @@ type
|
||||||
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
||||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||||
mMove, mWasMoved, mDestroy,
|
mMove, mWasMoved, mDestroy,
|
||||||
mDefault, mReset,
|
mDefault, mAccessEnv, mReset,
|
||||||
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
||||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||||
mOrdinal,
|
mOrdinal,
|
||||||
|
|
|
||||||
|
|
@ -1967,11 +1967,14 @@ proc genDestroy(p: BProc; n: PNode) =
|
||||||
[rdLoc(a), getTypeDesc(p.module, t.lastSon)])
|
[rdLoc(a), getTypeDesc(p.module, t.lastSon)])
|
||||||
else: discard "nothing to do"
|
else: discard "nothing to do"
|
||||||
else:
|
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"
|
discard "ignore calls to the default destructor"
|
||||||
|
|
||||||
proc genDispose(p: BProc; n: PNode) =
|
proc genDispose(p: BProc; n: PNode) =
|
||||||
when false:
|
when false:
|
||||||
let elemType = n[1].typ.skipTypes(abstractVarInst).lastSon
|
let elemType = n[1].typ.skipTypes(abstractVar).lastSon
|
||||||
|
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, n[1].skipAddr, a)
|
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 mWasMoved: genWasMoved(p, e)
|
||||||
of mMove: genMove(p, e, d)
|
of mMove: genMove(p, e, d)
|
||||||
of mDestroy: genDestroy(p, e)
|
of mDestroy: genDestroy(p, e)
|
||||||
|
of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0")
|
||||||
of mSlice:
|
of mSlice:
|
||||||
localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
|
localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
|
||||||
" 'toOpenArray' is only valid within a call expression")
|
" '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)
|
initLocExprSingleUse(p, ex, a)
|
||||||
line(p, cpsStmts, "(void)(" & a.r & ");\L")
|
line(p, cpsStmts, "(void)(" & a.r & ");\L")
|
||||||
of nkAsmStmt: genAsmStmt(p, n)
|
of nkAsmStmt: genAsmStmt(p, n)
|
||||||
of nkTryStmt:
|
of nkTryStmt, nkHiddenTryStmt:
|
||||||
if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
|
if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
|
||||||
genTryCpp(p, n, d)
|
genTryCpp(p, n, d)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -868,7 +868,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||||
of nkSym:
|
of nkSym:
|
||||||
# some path reads from 'result' before it was written to!
|
# some path reads from 'result' before it was written to!
|
||||||
if n.sym.kind == skResult: result = InitRequired
|
if n.sym.kind == skResult: result = InitRequired
|
||||||
of nkTryStmt:
|
of nkTryStmt, nkHiddenTryStmt:
|
||||||
# We need to watch out for the following problem:
|
# We need to watch out for the following problem:
|
||||||
# try:
|
# try:
|
||||||
# result = stuffThatRaises()
|
# result = stuffThatRaises()
|
||||||
|
|
|
||||||
|
|
@ -541,7 +541,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||||
|
|
||||||
if isExpr: result.add(ctx.newEnvVarAccess(tmp))
|
if isExpr: result.add(ctx.newEnvVarAccess(tmp))
|
||||||
|
|
||||||
of nkTryStmt:
|
of nkTryStmt, nkHiddenTryStmt:
|
||||||
var ns = false
|
var ns = false
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
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.transformBreaksInBlock(result[1], result[0], gotoOut)
|
||||||
result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
|
result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
|
||||||
|
|
||||||
of nkTryStmt:
|
of nkTryStmt, nkHiddenTryStmt:
|
||||||
# See explanation above about how this works
|
# See explanation above about how this works
|
||||||
ctx.hasExceptions = true
|
ctx.hasExceptions = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||||
of "taintmode": result = contains(conf.globalOptions, optTaintMode)
|
of "taintmode": result = contains(conf.globalOptions, optTaintMode)
|
||||||
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
|
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
|
||||||
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
|
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 "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||||
of "nilseqs": result = contains(conf.options, optNilSeqs)
|
of "nilseqs": result = contains(conf.options, optNilSeqs)
|
||||||
of "oldast": result = contains(conf.options, optOldAst)
|
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 "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
|
||||||
of "implicitstatic":
|
of "implicitstatic":
|
||||||
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
|
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
|
||||||
of "patterns":
|
of "patterns", "trmacros":
|
||||||
processOnOffSwitch(conf, {optPatterns}, arg, pass, info)
|
processOnOffSwitch(conf, {optTrMacros}, arg, pass, info)
|
||||||
of "opt":
|
of "opt":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
|
|
|
||||||
|
|
@ -665,7 +665,7 @@ proc gen(c: var Con; n: PNode) =
|
||||||
of nkReturnStmt: genReturn(c, n)
|
of nkReturnStmt: genReturn(c, n)
|
||||||
of nkRaiseStmt: genRaise(c, n)
|
of nkRaiseStmt: genRaise(c, n)
|
||||||
of nkBreakStmt: genBreak(c, n)
|
of nkBreakStmt: genBreak(c, n)
|
||||||
of nkTryStmt: genTry(c, n)
|
of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
|
||||||
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
|
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
|
||||||
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
|
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
|
||||||
for x in n: gen(c, x)
|
for x in n: gen(c, x)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import
|
||||||
ast, strutils, strtabs, options, msgs, os, ropes, idents,
|
ast, strutils, strtabs, options, msgs, os, ropes, idents,
|
||||||
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
|
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
|
||||||
packages/docutils/rst, packages/docutils/rstgen,
|
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,
|
typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets,
|
||||||
pathutils
|
pathutils
|
||||||
|
|
||||||
|
|
@ -760,6 +760,63 @@ proc exportSym(d: PDoc; s: PSym) =
|
||||||
"$1", [rope esc(d.target, s.name.s),
|
"$1", [rope esc(d.target, s.name.s),
|
||||||
rope changeFileExt(external, "html")])
|
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) =
|
proc generateDoc*(d: PDoc, n, orig: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCommentStmt: add(d.modDesc, genComment(d, n))
|
of nkCommentStmt: add(d.modDesc, genComment(d, n))
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,12 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
proc hloBody(c: PContext, n: PNode): PNode =
|
proc hloBody(c: PContext, n: PNode): PNode =
|
||||||
# fast exit:
|
# 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
|
c.hloLoopDetector = 0
|
||||||
result = hlo(c, n)
|
result = hlo(c, n)
|
||||||
|
|
||||||
proc hloStmt(c: PContext, n: PNode): PNode =
|
proc hloStmt(c: PContext, n: PNode): PNode =
|
||||||
# fast exit:
|
# 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
|
c.hloLoopDetector = 0
|
||||||
result = hlo(c, n)
|
result = hlo(c, n)
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@
|
||||||
## destroy(tmp.x); destroy(tmp.y)
|
## destroy(tmp.x); destroy(tmp.y)
|
||||||
##
|
##
|
||||||
|
|
||||||
##[
|
#[
|
||||||
From https://github.com/nim-lang/Nim/wiki/Destructors
|
From https://github.com/nim-lang/Nim/wiki/Destructors
|
||||||
|
|
||||||
Rule Pattern Transformed into
|
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
|
And the C++ optimizers don't sweat to optimize it for us, so we don't have
|
||||||
to do it.
|
to do it.
|
||||||
]##
|
]#
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
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:
|
if c.otherRead != nil:
|
||||||
m.add "; another read is done here: "
|
m.add "; another read is done here: "
|
||||||
m.add c.graph.config $ c.otherRead.info
|
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)
|
localError(c.graph.config, ri.info, errGenerated, m)
|
||||||
|
|
||||||
proc makePtrType(c: Con, baseType: PType): PType =
|
proc makePtrType(c: Con, baseType: PType): PType =
|
||||||
|
|
@ -329,7 +333,7 @@ template genOp(opr, opname, ri) =
|
||||||
elif op.ast[genericParamsPos].kind != nkEmpty:
|
elif op.ast[genericParamsPos].kind != nkEmpty:
|
||||||
globalError(c.graph.config, dest.info, "internal error: '" & opname &
|
globalError(c.graph.config, dest.info, "internal error: '" & opname &
|
||||||
"' operator is generic")
|
"' operator is generic")
|
||||||
patchHead op
|
#patchHead op
|
||||||
if sfError in op.flags: checkForErrorPragma(c, t, ri, opname)
|
if sfError in op.flags: checkForErrorPragma(c, t, ri, opname)
|
||||||
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
|
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
|
||||||
addrExp.add(dest)
|
addrExp.add(dest)
|
||||||
|
|
@ -343,7 +347,14 @@ proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||||
echo t.sink.id, " owner ", t.id
|
echo t.sink.id, " owner ", t.id
|
||||||
quit 1
|
quit 1
|
||||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
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 =
|
proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||||
if tfHasOwned in t.flags:
|
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 =
|
proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||||
when false: # defined(nimDebugDestroys):
|
when false: # defined(nimDebugDestroys):
|
||||||
echo "injecting into ", n
|
echo "injecting into ", n
|
||||||
|
if sfGeneratedOp in owner.flags: return n
|
||||||
var c: Con
|
var c: Con
|
||||||
c.owner = owner
|
c.owner = owner
|
||||||
c.destroys = newNodeI(nkStmtList, n.info)
|
c.destroys = newNodeI(nkStmtList, n.info)
|
||||||
|
|
@ -2387,7 +2387,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
genLineDir(p, n)
|
genLineDir(p, n)
|
||||||
gen(p, n.sons[0], r)
|
gen(p, n.sons[0], r)
|
||||||
of nkAsmStmt: genAsmOrEmitStmt(p, n)
|
of nkAsmStmt: genAsmOrEmitStmt(p, n)
|
||||||
of nkTryStmt: genTry(p, n, r)
|
of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r)
|
||||||
of nkRaiseStmt: genRaiseStmt(p, n)
|
of nkRaiseStmt: genRaiseStmt(p, n)
|
||||||
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
|
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
|
||||||
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
## This module implements lifting for type-bound operations
|
## This module implements lifting for type-bound operations
|
||||||
## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
|
## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
|
||||||
|
|
||||||
# included from sem.nim
|
# included from sempass2.nim
|
||||||
|
|
||||||
type
|
type
|
||||||
TLiftCtx = object
|
TLiftCtx = object
|
||||||
|
|
@ -20,9 +20,10 @@ type
|
||||||
fn: PSym
|
fn: PSym
|
||||||
asgnForType: PType
|
asgnForType: PType
|
||||||
recurse: bool
|
recurse: bool
|
||||||
|
c: PContext
|
||||||
|
|
||||||
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
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.}
|
info: TLineInfo): PSym {.discardable.}
|
||||||
|
|
||||||
proc at(a, i: PNode, elemType: PType): PNode =
|
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)
|
(tfHasGCedMem in t.flags or t.isGCedMem)
|
||||||
|
|
||||||
proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||||
field: PSym): bool =
|
field: var PSym): bool =
|
||||||
if tfHasAsgn in t.flags or useNoGc(c, t):
|
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
|
var op: PSym
|
||||||
if sameType(t, c.asgnForType):
|
if sameType(t, c.asgnForType):
|
||||||
# generate recursive call:
|
# generate recursive call:
|
||||||
|
|
@ -131,19 +142,29 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||||
else:
|
else:
|
||||||
op = field
|
op = field
|
||||||
if op == nil:
|
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:
|
if sfError in op.flags:
|
||||||
incl c.fn.flags, sfError
|
incl c.fn.flags, sfError
|
||||||
else:
|
else:
|
||||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||||
onUse(c.info, op)
|
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)
|
body.add newAsgnCall(c.graph, op, x, y)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
|
proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
|
||||||
var op = t.destructor
|
var op = t.destructor
|
||||||
if op == nil and useNoGc(c, t):
|
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 != nil
|
||||||
doAssert op == t.destructor
|
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 =
|
proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
case c.kind
|
case c.kind
|
||||||
of attachedDestructor:
|
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:
|
of attachedAsgn:
|
||||||
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
|
|
@ -221,10 +255,10 @@ proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
|
||||||
lenCall.typ = getSysType(g, x.info, tyInt)
|
lenCall.typ = getSysType(g, x.info, tyInt)
|
||||||
result.add lenCall
|
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)
|
let lenCall = genBuiltin(g, mLengthSeq, "len", y)
|
||||||
lenCall.typ = getSysType(g, x.info, tyInt)
|
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
|
result.add lenCall
|
||||||
|
|
||||||
proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
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
|
# var i = 0
|
||||||
# while i < y.len: dest[i] = y[i]; inc(i)
|
# while i < y.len: dest[i] = y[i]; inc(i)
|
||||||
# This is usually more efficient than a destroy/create pair.
|
# 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)
|
forallElements(c, t, body, x, y)
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
let moveCall = genBuiltin(c.graph, mMove, "move", x)
|
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)
|
body.add genIf(c, genVerbatim("dest@len != 0 && dest@region", c.info), deallocStmt)
|
||||||
|
|
||||||
proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
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) =
|
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -322,13 +372,34 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
call.sons[1] = y
|
call.sons[1] = y
|
||||||
body.add newAsgnStmt(x, call)
|
body.add newAsgnStmt(x, call)
|
||||||
elif optNimV2 in c.graph.config.globalOptions:
|
elif optNimV2 in c.graph.config.globalOptions:
|
||||||
|
let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x)
|
||||||
case c.kind
|
case c.kind
|
||||||
of attachedSink, attachedAsgn: discard
|
of attachedSink:
|
||||||
of attachedDestructor: discard
|
# 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")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
|
|
||||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
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) =
|
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case t.kind
|
case t.kind
|
||||||
|
|
@ -417,35 +488,35 @@ proc addParam(procType: PType; param: PSym) =
|
||||||
addSon(procType.n, newSymNode(param))
|
addSon(procType.n, newSymNode(param))
|
||||||
rawAddSon(procType, param.typ)
|
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
|
assert typ.kind == tyDistinct
|
||||||
let baseType = typ[0]
|
let baseType = typ[0]
|
||||||
case kind
|
case kind
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
if baseType.assignment == nil:
|
if baseType.assignment == nil:
|
||||||
discard liftBody(g, baseType, kind, info)
|
discard liftBody(c, baseType, kind, info)
|
||||||
typ.assignment = baseType.assignment
|
typ.assignment = baseType.assignment
|
||||||
result = typ.assignment
|
result = typ.assignment
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
if baseType.sink == nil:
|
if baseType.sink == nil:
|
||||||
discard liftBody(g, baseType, kind, info)
|
discard liftBody(c, baseType, kind, info)
|
||||||
typ.sink = baseType.sink
|
typ.sink = baseType.sink
|
||||||
result = typ.sink
|
result = typ.sink
|
||||||
of attachedDeepCopy:
|
of attachedDeepCopy:
|
||||||
if baseType.deepCopy == nil:
|
if baseType.deepCopy == nil:
|
||||||
discard liftBody(g, baseType, kind, info)
|
discard liftBody(c, baseType, kind, info)
|
||||||
typ.deepCopy = baseType.deepCopy
|
typ.deepCopy = baseType.deepCopy
|
||||||
result = typ.deepCopy
|
result = typ.deepCopy
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
if baseType.destructor == nil:
|
if baseType.destructor == nil:
|
||||||
discard liftBody(g, baseType, kind, info)
|
discard liftBody(c, baseType, kind, info)
|
||||||
typ.destructor = baseType.destructor
|
typ.destructor = baseType.destructor
|
||||||
result = typ.destructor
|
result = typ.destructor
|
||||||
|
|
||||||
proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
|
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
info: TLineInfo): PSym =
|
info: TLineInfo): PSym =
|
||||||
if typ.kind == tyDistinct:
|
if typ.kind == tyDistinct:
|
||||||
return liftBodyDistinctType(g, typ, kind, info)
|
return liftBodyDistinctType(c, typ, kind, info)
|
||||||
when false:
|
when false:
|
||||||
var typ = typ
|
var typ = typ
|
||||||
if c.config.selectedGC == gcDestructors and typ.kind == tySequence:
|
if c.config.selectedGC == gcDestructors and typ.kind == tySequence:
|
||||||
|
|
@ -454,8 +525,10 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
|
||||||
|
|
||||||
var a: TLiftCtx
|
var a: TLiftCtx
|
||||||
a.info = info
|
a.info = info
|
||||||
a.graph = g
|
a.graph = c.graph
|
||||||
a.kind = kind
|
a.kind = kind
|
||||||
|
a.c = c
|
||||||
|
let g = c.graph
|
||||||
let body = newNodeI(nkStmtList, info)
|
let body = newNodeI(nkStmtList, info)
|
||||||
let procname = case kind
|
let procname = case kind
|
||||||
of attachedAsgn: getIdent(g.cache, "=")
|
of attachedAsgn: getIdent(g.cache, "=")
|
||||||
|
|
@ -477,7 +550,7 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
|
||||||
if kind != attachedDestructor:
|
if kind != attachedDestructor:
|
||||||
result.typ.addParam src
|
result.typ.addParam src
|
||||||
|
|
||||||
if optNimV2 in g.config.globalOptions:
|
# register this operation already:
|
||||||
case kind
|
case kind
|
||||||
of attachedAsgn: typ.assignment = result
|
of attachedAsgn: typ.assignment = result
|
||||||
of attachedSink: typ.sink = result
|
of attachedSink: typ.sink = result
|
||||||
|
|
@ -485,18 +558,6 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
|
||||||
of attachedDestructor: typ.destructor = result
|
of attachedDestructor: typ.destructor = result
|
||||||
|
|
||||||
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
|
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)
|
var n = newNodeI(nkProcDef, info, bodyPos+1)
|
||||||
for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
|
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
|
n.sons[bodyPos] = body
|
||||||
result.ast = n
|
result.ast = n
|
||||||
incl result.flags, sfFromGeneric
|
incl result.flags, sfFromGeneric
|
||||||
|
incl result.flags, sfGeneratedOp
|
||||||
|
|
||||||
|
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
|
||||||
proc getAsgnOrLiftBody(g: ModuleGraph; typ: PType; info: TLineInfo): PSym =
|
|
||||||
let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
|
let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
|
||||||
result = t.assignment
|
result = t.assignment
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
result = liftBody(g, t, attachedAsgn, info)
|
result = liftBody(c, t, attachedAsgn, info)
|
||||||
|
|
||||||
proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode =
|
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
|
||||||
let a = getAsgnOrLiftBody(g, dest.typ, dest.info)
|
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
|
||||||
result = newAsgnCall(g, a, dest, src)
|
result = newAsgnCall(c.graph, a, dest, src)
|
||||||
|
|
||||||
proc liftTypeBoundOps*(g: ModuleGraph; typ: PType; info: TLineInfo) =
|
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
|
## In the semantic pass this is called in strategic places
|
||||||
## to ensure we lift assignment, destructors and moves properly.
|
## to ensure we lift assignment, destructors and moves properly.
|
||||||
## The later 'destroyer' pass depends on it.
|
## The later 'injectdestructors' pass depends on it.
|
||||||
if not hasDestructor(typ): return
|
if typ == nil or {tfCheckedForDestructor, tfHasMeta} * typ.flags != {}: return
|
||||||
when false:
|
incl typ.flags, tfCheckedForDestructor
|
||||||
# do not produce wrong liftings while we're still instantiating generics:
|
# multiple cases are to distinguish here:
|
||||||
# now disabled; breaks topttree.nim!
|
# 1. we don't know yet if 'typ' has a nontrival destructor.
|
||||||
if c.typesWithOps.len > 0: return
|
# 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})
|
let typ = typ.skipTypes({tyGenericInst, tyAlias})
|
||||||
# we generate the destructor first so that other operators can depend on it:
|
# we generate the destructor first so that other operators can depend on it:
|
||||||
if typ.destructor == nil:
|
if typ.destructor == nil:
|
||||||
liftBody(g, typ, attachedDestructor, info)
|
liftBody(c, typ, attachedDestructor, info)
|
||||||
|
else:
|
||||||
|
inst(typ.destructor, typ)
|
||||||
if typ.assignment == nil:
|
if typ.assignment == nil:
|
||||||
liftBody(g, typ, attachedAsgn, info)
|
liftBody(c, typ, attachedAsgn, info)
|
||||||
|
else:
|
||||||
|
inst(typ.assignment, typ)
|
||||||
if typ.sink == nil:
|
if typ.sink == nil:
|
||||||
liftBody(g, typ, attachedSink, info)
|
liftBody(c, typ, attachedSink, info)
|
||||||
|
else:
|
||||||
|
inst(typ.sink, typ)
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
|
||||||
addSon(result, lit)
|
addSon(result, lit)
|
||||||
|
|
||||||
proc newTryFinally*(body, final: PNode): PNode =
|
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 =
|
proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||||
let value = n.lastSon
|
let value = n.lastSon
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ type # please make sure we have under 32 options
|
||||||
optProfiler, # profiler turned on
|
optProfiler, # profiler turned on
|
||||||
optImplicitStatic, # optimization: implicit at compile time
|
optImplicitStatic, # optimization: implicit at compile time
|
||||||
# evaluation
|
# evaluation
|
||||||
optPatterns, # en/disable pattern matching
|
optTrMacros, # en/disable pattern matching
|
||||||
optMemTracker,
|
optMemTracker,
|
||||||
optLaxStrings,
|
optLaxStrings,
|
||||||
optNilSeqs,
|
optNilSeqs,
|
||||||
|
|
@ -273,7 +273,7 @@ const
|
||||||
DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
|
DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
|
||||||
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
|
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
|
||||||
optHints, optStackTrace, optLineTrace,
|
optHints, optStackTrace, optLineTrace,
|
||||||
optPatterns, optNilCheck, optMoveCheck}
|
optTrMacros, optNilCheck, optMoveCheck}
|
||||||
DefaultGlobalOptions* = {optThreadAnalysis}
|
DefaultGlobalOptions* = {optThreadAnalysis}
|
||||||
|
|
||||||
proc getSrcTimestamp(): DateTime =
|
proc getSrcTimestamp(): DateTime =
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ const
|
||||||
wDeadCodeElimUnused, # deprecated, always on
|
wDeadCodeElimUnused, # deprecated, always on
|
||||||
wDeprecated,
|
wDeprecated,
|
||||||
wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||||
wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto,
|
wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
|
||||||
wInjectStmt, wDeprecated, wExperimental, wThis}
|
wInjectStmt, wDeprecated, wExperimental, wThis}
|
||||||
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||||
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
||||||
|
|
@ -350,7 +350,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
|
||||||
of wMemTracker: {optMemTracker}
|
of wMemTracker: {optMemTracker}
|
||||||
of wByRef: {optByRef}
|
of wByRef: {optByRef}
|
||||||
of wImplicitStatic: {optImplicitStatic}
|
of wImplicitStatic: {optImplicitStatic}
|
||||||
of wPatterns: {optPatterns}
|
of wPatterns, wTrMacros: {optTrMacros}
|
||||||
else: {}
|
else: {}
|
||||||
|
|
||||||
proc processExperimental(c: PContext; n: PNode) =
|
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,
|
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
|
||||||
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
||||||
wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler,
|
wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler,
|
||||||
wFloatchecks, wNanChecks, wInfChecks, wPatterns:
|
wFloatchecks, wNanChecks, wInfChecks, wPatterns, wTrMacros:
|
||||||
processOption(c, it, c.config.options)
|
processOption(c, it, c.config.options)
|
||||||
of wStacktrace, wLinetrace:
|
of wStacktrace, wLinetrace:
|
||||||
if sym.kind in {skProc, skMethod, skConverter}:
|
if sym.kind in {skProc, skMethod, skConverter}:
|
||||||
|
|
|
||||||
|
|
@ -1295,7 +1295,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
of nkWhileStmt: gwhile(g, n)
|
of nkWhileStmt: gwhile(g, n)
|
||||||
of nkPragmaBlock: gpragmaBlock(g, n)
|
of nkPragmaBlock: gpragmaBlock(g, n)
|
||||||
of nkCaseStmt, nkRecCase: gcase(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 nkForStmt, nkParForStmt: gfor(g, n)
|
||||||
of nkBlockStmt, nkBlockExpr: gblock(g, n)
|
of nkBlockStmt, nkBlockExpr: gblock(g, n)
|
||||||
of nkStaticStmt: gstaticStmt(g, n)
|
of nkStaticStmt: gstaticStmt(g, n)
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||||
result = buildEchoStmt(c, result)
|
result = buildEchoStmt(c, result)
|
||||||
if c.config.cmd == cmdIdeTools:
|
if c.config.cmd == cmdIdeTools:
|
||||||
appendToModule(c.module, result)
|
appendToModule(c.module, result)
|
||||||
trackTopLevelStmt(c.graph, c.module, result)
|
trackTopLevelStmt(c, c.module, result)
|
||||||
|
|
||||||
proc recoverContext(c: PContext) =
|
proc recoverContext(c: PContext) =
|
||||||
# clean up in case of a semantic error: We clean up the stacks, etc. This is
|
# clean up in case of a semantic error: We clean up the stacks, etc. This is
|
||||||
|
|
|
||||||
|
|
@ -298,10 +298,12 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
result = typ
|
result = typ
|
||||||
else:
|
else:
|
||||||
result = newTypeS(tyTypeDesc, c)
|
result = newTypeS(tyTypeDesc, c)
|
||||||
|
incl result.flags, tfCheckedForDestructor
|
||||||
result.addSonSkipIntLit(typ)
|
result.addSonSkipIntLit(typ)
|
||||||
|
|
||||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||||
let typedesc = newTypeS(tyTypeDesc, c)
|
let typedesc = newTypeS(tyTypeDesc, c)
|
||||||
|
incl typedesc.flags, tfCheckedForDestructor
|
||||||
typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
|
typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
|
||||||
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
|
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
|
||||||
c.config.options).linkTo(typedesc)
|
c.config.options).linkTo(typedesc)
|
||||||
|
|
|
||||||
|
|
@ -815,7 +815,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
if result.typ != nil and
|
if result.typ != nil and
|
||||||
not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
|
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)
|
#result = patchResolvedTypeBoundOp(c, result)
|
||||||
if c.matchedConcept == nil:
|
if c.matchedConcept == nil:
|
||||||
result = evalAtCompileTime(c, result)
|
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)
|
typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
|
||||||
|
|
||||||
n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
|
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)
|
#liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info)
|
||||||
|
|
||||||
fixAbstractType(c, n)
|
fixAbstractType(c, n)
|
||||||
|
|
@ -2617,7 +2617,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkTypeSection: result = semTypeSection(c, n)
|
of nkTypeSection: result = semTypeSection(c, n)
|
||||||
of nkDiscardStmt: result = semDiscard(c, n)
|
of nkDiscardStmt: result = semDiscard(c, n)
|
||||||
of nkWhileStmt: result = semWhile(c, n, flags)
|
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 nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
|
||||||
of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
|
of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
|
||||||
of nkCaseStmt: result = semCase(c, n, flags)
|
of nkCaseStmt: result = semCase(c, n, flags)
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
addTempDecl(c, n.sons[0], skLabel)
|
addTempDecl(c, n.sons[0], skLabel)
|
||||||
n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
|
n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
of nkTryStmt:
|
of nkTryStmt, nkHiddenTryStmt:
|
||||||
checkMinSonsLen(n, 2, c.config)
|
checkMinSonsLen(n, 2, c.config)
|
||||||
n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
|
n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
|
||||||
for i in countup(1, sonsLen(n)-1):
|
for i in countup(1, sonsLen(n)-1):
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
|
||||||
freshGenSyms(b, result, orig, symMap)
|
freshGenSyms(b, result, orig, symMap)
|
||||||
b = semProcBody(c, b)
|
b = semProcBody(c, b)
|
||||||
result.ast[bodyPos] = hloBody(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)
|
excl(result.flags, sfForward)
|
||||||
dec c.inGenericInst
|
dec c.inGenericInst
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -428,4 +428,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
|
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")
|
localError(c.config, n.info, "finalizer must be a direct reference to a procedure")
|
||||||
result = n
|
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
|
else: result = n
|
||||||
|
|
|
||||||
|
|
@ -352,7 +352,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||||
if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
|
if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
|
||||||
c.addSlice(n, n[0], n[1], n[1])
|
c.addSlice(n, n[0], n[1], n[1])
|
||||||
analyseSons(c, n)
|
analyseSons(c, n)
|
||||||
of nkReturnStmt, nkRaiseStmt, nkTryStmt:
|
of nkReturnStmt, nkRaiseStmt, nkTryStmt, nkHiddenTryStmt:
|
||||||
localError(c.graph.config, n.info, "invalid control flow for 'parallel'")
|
localError(c.graph.config, n.info, "invalid control flow for 'parallel'")
|
||||||
# 'break' that leaves the 'parallel' section is not valid either
|
# 'break' that leaves the 'parallel' section is not valid either
|
||||||
# or maybe we should generate a 'try' XXX
|
# or maybe we should generate a 'try' XXX
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||||
wordrecg, strutils, options, guards, lineinfos, semfold,
|
wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
|
||||||
modulegraphs
|
modulegraphs, lowerings, sigmatch
|
||||||
|
|
||||||
when not defined(leanCompiler):
|
when not defined(leanCompiler):
|
||||||
import writetracking
|
import writetracking
|
||||||
|
|
@ -18,11 +18,30 @@ when not defined(leanCompiler):
|
||||||
when defined(useDfa):
|
when defined(useDfa):
|
||||||
import dfa
|
import dfa
|
||||||
|
|
||||||
# Second semantic checking pass over the AST. Necessary because the old
|
include liftdestructors
|
||||||
# way had some inherent problems. Performs:
|
|
||||||
#
|
#[ Second semantic checking pass over the AST. Necessary because the old
|
||||||
# * effect+exception tracking
|
way had some inherent problems. Performs:
|
||||||
# * "usage before definition" checking
|
|
||||||
|
* 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 -------------------------
|
# ------------------------ exception and tag tracking -------------------------
|
||||||
|
|
||||||
|
|
@ -60,6 +79,7 @@ type
|
||||||
maxLockLevel, currLockLevel: TLockLevel
|
maxLockLevel, currLockLevel: TLockLevel
|
||||||
config: ConfigRef
|
config: ConfigRef
|
||||||
graph: ModuleGraph
|
graph: ModuleGraph
|
||||||
|
c: PContext
|
||||||
PEffects = var TEffects
|
PEffects = var TEffects
|
||||||
|
|
||||||
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
||||||
|
|
@ -416,73 +436,6 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
|
||||||
# list the computed effects up to here:
|
# list the computed effects up to here:
|
||||||
listEffects(tracked)
|
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 == {}
|
template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
|
||||||
|
|
||||||
proc importedFromC(n: PNode): bool =
|
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.
|
# Here we add a `Exception` tag in order to cover both the cases.
|
||||||
addEffect(tracked, createRaise(tracked.graph, n))
|
addEffect(tracked, createRaise(tracked.graph, n))
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
|
|
||||||
return
|
|
||||||
# p's effects are ours too:
|
# p's effects are ours too:
|
||||||
var a = n.sons[0]
|
var a = n.sons[0]
|
||||||
let op = a.typ
|
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:
|
if a.kind == nkCast and a[1].typ.kind == tyProc:
|
||||||
a = a[1]
|
a = a[1]
|
||||||
# XXX: in rare situations, templates and macros will reach here after
|
# 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])
|
addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
|
||||||
notNilCheck(tracked, n.sons[1], n.sons[0].typ)
|
notNilCheck(tracked, n.sons[1], n.sons[0].typ)
|
||||||
when false: cstringCheck(tracked, n)
|
when false: cstringCheck(tracked, n)
|
||||||
|
createTypeBoundOps(tracked.c, n[0].typ, n.info)
|
||||||
of nkVarSection, nkLetSection:
|
of nkVarSection, nkLetSection:
|
||||||
for child in n:
|
for child in n:
|
||||||
let last = lastSon(child)
|
let last = lastSon(child)
|
||||||
if last.kind != nkEmpty: track(tracked, last)
|
if last.kind != nkEmpty: track(tracked, last)
|
||||||
|
createTypeBoundOps(tracked.c, child[0].typ, child.info)
|
||||||
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
||||||
for i in 0 .. child.len-3:
|
for i in 0 .. child.len-3:
|
||||||
initVar(tracked, child.sons[i], volatileCheck=false)
|
initVar(tracked, child.sons[i], volatileCheck=false)
|
||||||
|
|
@ -965,7 +922,7 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
|
||||||
effects[tagEffects] = tagsSpec
|
effects[tagEffects] = tagsSpec
|
||||||
effects[pragmasEffects] = n
|
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)
|
newSeq(effects.sons, effectListLen)
|
||||||
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
|
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
|
||||||
effects.sons[tagEffects] = 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.locked = @[]
|
||||||
t.graph = g
|
t.graph = g
|
||||||
t.config = g.config
|
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]
|
var effects = s.typ.n.sons[0]
|
||||||
if effects.kind != nkEffectList: return
|
if effects.kind != nkEffectList: return
|
||||||
# effects already computed?
|
# effects already computed?
|
||||||
|
|
@ -992,7 +951,7 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
|
||||||
if effects.len == effectListLen: return
|
if effects.len == effectListLen: return
|
||||||
|
|
||||||
var t: TEffects
|
var t: TEffects
|
||||||
initEffects(g, effects, s, t)
|
initEffects(g, effects, s, t, c)
|
||||||
track(t, body)
|
track(t, body)
|
||||||
if not isEmptyType(s.typ.sons[0]) and
|
if not isEmptyType(s.typ.sons[0]) and
|
||||||
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
|
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
|
||||||
|
|
@ -1043,12 +1002,13 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
|
||||||
dataflowAnalysis(s, body)
|
dataflowAnalysis(s, body)
|
||||||
when false: trackWrites(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,
|
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||||
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
||||||
return
|
return
|
||||||
|
let g = c.graph
|
||||||
var effects = newNode(nkEffectList, n.info)
|
var effects = newNode(nkEffectList, n.info)
|
||||||
var t: TEffects
|
var t: TEffects
|
||||||
initEffects(g, effects, module, t)
|
initEffects(g, effects, module, t, c)
|
||||||
t.isToplevel = true
|
t.isToplevel = true
|
||||||
track(t, n)
|
track(t, n)
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,7 @@ proc checkNilable(c: PContext; v: PSym) =
|
||||||
elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags:
|
elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags:
|
||||||
message(c.config, v.info, warnProveInit, v.name.s)
|
message(c.config, v.info, warnProveInit, v.name.s)
|
||||||
|
|
||||||
include liftdestructors
|
#include liftdestructors
|
||||||
|
|
||||||
proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
|
proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
|
||||||
let L = identDefs.len
|
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:
|
# this can only happen for errornous var statements:
|
||||||
if typ == nil: continue
|
if typ == nil: continue
|
||||||
typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
|
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)
|
instAllTypeBoundOp(c, a.info)
|
||||||
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||||
if a.kind == nkVarTuple:
|
if a.kind == nkVarTuple:
|
||||||
|
|
@ -1265,7 +1265,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||||
checkConstructedType(c.config, s.info, s.typ)
|
checkConstructedType(c.config, s.info, s.typ)
|
||||||
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
|
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
|
||||||
checkForMetaFields(c, s.typ.n)
|
checkForMetaFields(c, s.typ.n)
|
||||||
instAllTypeBoundOp(c, n.info)
|
#instAllTypeBoundOp(c, n.info)
|
||||||
|
|
||||||
|
|
||||||
proc semAllTypeSections(c: PContext; n: PNode): PNode =
|
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)
|
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
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)
|
popProcCon(c)
|
||||||
elif efOperand notin flags:
|
elif efOperand notin flags:
|
||||||
localError(c.config, n.info, errGenericLambdaNotAllowed)
|
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)
|
addResult(c, n.typ.sons[0], n.info, skProc)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
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)
|
popProcCon(c)
|
||||||
popOwner(c)
|
popOwner(c)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
@ -1552,6 +1552,14 @@ proc canonType(c: PContext, t: PType): PType =
|
||||||
result = t
|
result = t
|
||||||
|
|
||||||
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
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
|
let name = s.name.s.normalize
|
||||||
case name
|
case name
|
||||||
of "=destroy":
|
of "=destroy":
|
||||||
|
|
@ -1569,8 +1577,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
if obj.destructor.isNil:
|
if obj.destructor.isNil:
|
||||||
obj.destructor = s
|
obj.destructor = s
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated,
|
prevDestructor(c, obj.destructor, obj, n.info)
|
||||||
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
|
||||||
noError = true
|
noError = true
|
||||||
if obj.owner.getModule != s.getModule:
|
if obj.owner.getModule != s.getModule:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
|
|
@ -1635,8 +1642,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
if opr[].isNil:
|
if opr[].isNil:
|
||||||
opr[] = s
|
opr[] = s
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated,
|
prevDestructor(c, opr[], obj, n.info)
|
||||||
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
|
|
||||||
if obj.owner.getModule != s.getModule:
|
if obj.owner.getModule != s.getModule:
|
||||||
localError(c.config, n.info, errGenerated,
|
localError(c.config, n.info, errGenerated,
|
||||||
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||||
|
|
@ -1862,7 +1868,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||||
# context as it may even be evaluated 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:
|
else:
|
||||||
if s.typ.sons[0] != nil and kind != skIterator:
|
if s.typ.sons[0] != nil and kind != skIterator:
|
||||||
addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
|
addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
|
||||||
|
|
|
||||||
|
|
@ -391,7 +391,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
n.sons[0] = newSymNode(s, n.sons[0].info)
|
n.sons[0] = newSymNode(s, n.sons[0].info)
|
||||||
n.sons[1] = semTemplBody(c, n.sons[1])
|
n.sons[1] = semTemplBody(c, n.sons[1])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
of nkTryStmt:
|
of nkTryStmt, nkHiddenTryStmt:
|
||||||
checkMinSonsLen(n, 2, c.c.config)
|
checkMinSonsLen(n, 2, c.c.config)
|
||||||
n.sons[0] = semTemplBodyScope(c, n.sons[0])
|
n.sons[0] = semTemplBodyScope(c, n.sons[0])
|
||||||
for i in countup(1, sonsLen(n)-1):
|
for i in countup(1, sonsLen(n)-1):
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
|
||||||
|
|
||||||
proc newConstraint(c: PContext, k: TTypeKind): PType =
|
proc newConstraint(c: PContext, k: TTypeKind): PType =
|
||||||
result = newTypeS(tyBuiltInTypeClass, c)
|
result = newTypeS(tyBuiltInTypeClass, c)
|
||||||
|
result.flags.incl tfCheckedForDestructor
|
||||||
result.addSonSkipIntLit(newTypeS(k, c))
|
result.addSonSkipIntLit(newTypeS(k, c))
|
||||||
|
|
||||||
proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
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:
|
paramTypId.id == getIdent(c.cache, "typedesc").id:
|
||||||
# XXX Why doesn't this check for tyTypeDesc instead?
|
# XXX Why doesn't this check for tyTypeDesc instead?
|
||||||
paramTypId = nil
|
paramTypId = nil
|
||||||
result = addImplicitGeneric(
|
let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base])
|
||||||
c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
|
incl t.flags, tfCheckedForDestructor
|
||||||
|
result = addImplicitGeneric(t)
|
||||||
|
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
if paramType.sonsLen == 1:
|
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
|
# surprising behavior. We must instead fix the expected type of
|
||||||
# the proc to be the unbound typedesc type:
|
# the proc to be the unbound typedesc type:
|
||||||
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
|
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
|
||||||
|
typ.flags.incl tfCheckedForDestructor
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# if def.typ != nil and def.typ.kind != tyNone:
|
# 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]
|
inherited = n[2]
|
||||||
|
|
||||||
result = newOrPrevType(tyUserTypeClass, prev, c)
|
result = newOrPrevType(tyUserTypeClass, prev, c)
|
||||||
|
result.flags.incl tfCheckedForDestructor
|
||||||
var owner = getCurrOwner(c)
|
var owner = getCurrOwner(c)
|
||||||
var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType])
|
var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType])
|
||||||
result.sons = @[candidateTypeSlot]
|
result.sons = @[candidateTypeSlot]
|
||||||
|
|
@ -1432,7 +1436,9 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if modifier != tyNone:
|
if modifier != tyNone:
|
||||||
dummyName = param[0]
|
dummyName = param[0]
|
||||||
dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot)
|
dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot)
|
||||||
if modifier == tyTypeDesc: dummyType.flags.incl tfConceptMatchedTypeSym
|
if modifier == tyTypeDesc:
|
||||||
|
dummyType.flags.incl tfConceptMatchedTypeSym
|
||||||
|
dummyType.flags.incl tfCheckedForDestructor
|
||||||
else:
|
else:
|
||||||
dummyName = param
|
dummyName = param
|
||||||
dummyType = candidateTypeSlot
|
dummyType = candidateTypeSlot
|
||||||
|
|
@ -1752,7 +1758,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
n.typ = result
|
n.typ = result
|
||||||
dec c.inTypeContext
|
dec c.inTypeContext
|
||||||
if c.inTypeContext == 0:
|
if false: # c.inTypeContext == 0:
|
||||||
#if $n == "var seq[StackTraceEntry]":
|
#if $n == "var seq[StackTraceEntry]":
|
||||||
# echo "begin ", n
|
# echo "begin ", n
|
||||||
instAllTypeBoundOp(c, n.info)
|
instAllTypeBoundOp(c, n.info)
|
||||||
|
|
@ -1779,24 +1785,28 @@ proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
|
||||||
else:
|
else:
|
||||||
discard
|
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) =
|
proc processMagicType(c: PContext, m: PSym) =
|
||||||
case m.magic
|
case m.magic
|
||||||
of mInt: setMagicType(c.config, m, tyInt, c.config.target.intSize)
|
of mInt: setMagicIntegral(c.config, m, tyInt, c.config.target.intSize)
|
||||||
of mInt8: setMagicType(c.config, m, tyInt8, 1)
|
of mInt8: setMagicIntegral(c.config, m, tyInt8, 1)
|
||||||
of mInt16: setMagicType(c.config, m, tyInt16, 2)
|
of mInt16: setMagicIntegral(c.config, m, tyInt16, 2)
|
||||||
of mInt32: setMagicType(c.config, m, tyInt32, 4)
|
of mInt32: setMagicIntegral(c.config, m, tyInt32, 4)
|
||||||
of mInt64: setMagicType(c.config, m, tyInt64, 8)
|
of mInt64: setMagicIntegral(c.config, m, tyInt64, 8)
|
||||||
of mUInt: setMagicType(c.config, m, tyUInt, c.config.target.intSize)
|
of mUInt: setMagicIntegral(c.config, m, tyUInt, c.config.target.intSize)
|
||||||
of mUInt8: setMagicType(c.config, m, tyUInt8, 1)
|
of mUInt8: setMagicIntegral(c.config, m, tyUInt8, 1)
|
||||||
of mUInt16: setMagicType(c.config, m, tyUInt16, 2)
|
of mUInt16: setMagicIntegral(c.config, m, tyUInt16, 2)
|
||||||
of mUInt32: setMagicType(c.config, m, tyUInt32, 4)
|
of mUInt32: setMagicIntegral(c.config, m, tyUInt32, 4)
|
||||||
of mUInt64: setMagicType(c.config, m, tyUInt64, 8)
|
of mUInt64: setMagicIntegral(c.config, m, tyUInt64, 8)
|
||||||
of mFloat: setMagicType(c.config, m, tyFloat, c.config.target.floatSize)
|
of mFloat: setMagicIntegral(c.config, m, tyFloat, c.config.target.floatSize)
|
||||||
of mFloat32: setMagicType(c.config, m, tyFloat32, 4)
|
of mFloat32: setMagicIntegral(c.config, m, tyFloat32, 4)
|
||||||
of mFloat64: setMagicType(c.config, m, tyFloat64, 8)
|
of mFloat64: setMagicIntegral(c.config, m, tyFloat64, 8)
|
||||||
of mFloat128: setMagicType(c.config, m, tyFloat128, 16)
|
of mFloat128: setMagicIntegral(c.config, m, tyFloat128, 16)
|
||||||
of mBool: setMagicType(c.config, m, tyBool, 1)
|
of mBool: setMagicIntegral(c.config, m, tyBool, 1)
|
||||||
of mChar: setMagicType(c.config, m, tyChar, 1)
|
of mChar: setMagicIntegral(c.config, m, tyChar, 1)
|
||||||
of mString:
|
of mString:
|
||||||
setMagicType(c.config, m, tyString, szUncomputedSize)
|
setMagicType(c.config, m, tyString, szUncomputedSize)
|
||||||
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
|
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
|
||||||
|
|
@ -1804,29 +1814,29 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||||
if c.config.selectedGc == gcDestructors:
|
if c.config.selectedGc == gcDestructors:
|
||||||
incl m.typ.flags, tfHasAsgn
|
incl m.typ.flags, tfHasAsgn
|
||||||
of mCstring:
|
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))
|
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:
|
of mEmptySet:
|
||||||
setMagicType(c.config, m, tySet, 1)
|
setMagicIntegral(c.config, m, tySet, 1)
|
||||||
rawAddSon(m.typ, newTypeS(tyEmpty, c))
|
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 mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize)
|
||||||
of mExpr:
|
of mExpr:
|
||||||
if m.name.s == "auto":
|
if m.name.s == "auto":
|
||||||
setMagicType(c.config, m, tyAnything, 0)
|
setMagicIntegral(c.config, m, tyAnything, 0)
|
||||||
else:
|
else:
|
||||||
setMagicType(c.config, m, tyExpr, 0)
|
setMagicIntegral(c.config, m, tyExpr, 0)
|
||||||
of mStmt:
|
of mStmt:
|
||||||
setMagicType(c.config, m, tyStmt, 0)
|
setMagicIntegral(c.config, m, tyStmt, 0)
|
||||||
of mTypeDesc, mType:
|
of mTypeDesc, mType:
|
||||||
setMagicType(c.config, m, tyTypeDesc, 0)
|
setMagicIntegral(c.config, m, tyTypeDesc, 0)
|
||||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||||
of mStatic:
|
of mStatic:
|
||||||
setMagicType(c.config, m, tyStatic, 0)
|
setMagicType(c.config, m, tyStatic, 0)
|
||||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||||
of mVoidType:
|
of mVoidType:
|
||||||
setMagicType(c.config, m, tyVoid, 0)
|
setMagicIntegral(c.config, m, tyVoid, 0)
|
||||||
of mArray:
|
of mArray:
|
||||||
setMagicType(c.config, m, tyArray, szUncomputedSize)
|
setMagicType(c.config, m, tyArray, szUncomputedSize)
|
||||||
of mOpenArray:
|
of mOpenArray:
|
||||||
|
|
@ -1834,12 +1844,12 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||||
of mVarargs:
|
of mVarargs:
|
||||||
setMagicType(c.config, m, tyVarargs, szUncomputedSize)
|
setMagicType(c.config, m, tyVarargs, szUncomputedSize)
|
||||||
of mRange:
|
of mRange:
|
||||||
setMagicType(c.config, m, tyRange, szUncomputedSize)
|
setMagicIntegral(c.config, m, tyRange, szUncomputedSize)
|
||||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||||
of mSet:
|
of mSet:
|
||||||
setMagicType(c.config, m, tySet, szUncomputedSize)
|
setMagicIntegral(c.config, m, tySet, szUncomputedSize)
|
||||||
of mUncheckedArray:
|
of mUncheckedArray:
|
||||||
setMagicType(c.config, m, tyUncheckedArray, szUncomputedSize)
|
setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize)
|
||||||
of mSeq:
|
of mSeq:
|
||||||
setMagicType(c.config, m, tySequence, szUncomputedSize)
|
setMagicType(c.config, m, tySequence, szUncomputedSize)
|
||||||
if c.config.selectedGc == gcDestructors:
|
if c.config.selectedGc == gcDestructors:
|
||||||
|
|
@ -1849,10 +1859,11 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||||
of mOpt:
|
of mOpt:
|
||||||
setMagicType(c.config, m, tyOpt, szUncomputedSize)
|
setMagicType(c.config, m, tyOpt, szUncomputedSize)
|
||||||
of mOrdinal:
|
of mOrdinal:
|
||||||
setMagicType(c.config, m, tyOrdinal, szUncomputedSize)
|
setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize)
|
||||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||||
of mPNimrodNode:
|
of mPNimrodNode:
|
||||||
incl m.typ.flags, tfTriggersCompileTime
|
incl m.typ.flags, tfTriggersCompileTime
|
||||||
|
incl m.typ.flags, tfCheckedForDestructor
|
||||||
of mException: discard
|
of mException: discard
|
||||||
of mBuiltinType:
|
of mBuiltinType:
|
||||||
case m.name.s
|
case m.name.s
|
||||||
|
|
@ -1886,6 +1897,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
if typ.kind == tyTypeDesc:
|
if typ.kind == tyTypeDesc:
|
||||||
if typ.sons[0].kind == tyNone:
|
if typ.sons[0].kind == tyNone:
|
||||||
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
|
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
|
||||||
|
incl typ.flags, tfCheckedForDestructor
|
||||||
else:
|
else:
|
||||||
typ = semGenericConstraints(c, typ)
|
typ = semGenericConstraints(c, typ)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
when false:
|
when false:
|
||||||
if newDestructors:
|
if newDestructors:
|
||||||
result.assignment = nil
|
result.assignment = nil
|
||||||
#result.destructor = nil
|
result.destructor = nil
|
||||||
result.sink = nil
|
result.sink = nil
|
||||||
|
|
||||||
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
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]
|
# adding myseq for myseq[system.int]
|
||||||
# sigmatch: Formal myseq[=destroy.T] real myseq[system.int]
|
# sigmatch: Formal myseq[=destroy.T] real myseq[system.int]
|
||||||
#echo "DESTROY: adding ", typeToString(newbody), " for ", typeToString(result, preferDesc)
|
#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)
|
let mm = skipTypes(bbody, abstractPtrs)
|
||||||
if tfFromGeneric notin mm.flags:
|
if tfFromGeneric notin mm.flags:
|
||||||
# bug #5479, prevent endless recursions here:
|
# bug #5479, prevent endless recursions here:
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
import
|
import
|
||||||
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
|
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
|
||||||
idents, renderer, types, passes, semfold, magicsys, cgmeth,
|
idents, renderer, types, passes, semfold, magicsys, cgmeth,
|
||||||
sempass2, lowerings, destroyer, liftlocals,
|
sempass2, lowerings, injectdestructors, liftlocals,
|
||||||
modulegraphs, lineinfos
|
modulegraphs, lineinfos
|
||||||
|
|
||||||
proc transformBody*(g: ModuleGraph, prc: PSym, cache = true;
|
proc transformBody*(g: ModuleGraph, prc: PSym, cache = true;
|
||||||
|
|
|
||||||
|
|
@ -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
|
let key = if n.kind in nkPragmaCallKinds and n.len > 0: n.sons[0] else: n
|
||||||
if key.kind == nkIdent: result = whichKeyword(key.ident)
|
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) =
|
proc unnestStmts(n, result: PNode) =
|
||||||
if n.kind == nkStmtList:
|
if n.kind == nkStmtList:
|
||||||
for x in items(n): unnestStmts(x, result)
|
for x in items(n): unnestStmts(x, result)
|
||||||
|
|
|
||||||
|
|
@ -2031,7 +2031,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
of nkBreakStmt:
|
of nkBreakStmt:
|
||||||
unused(c, n, dest)
|
unused(c, n, dest)
|
||||||
genBreak(c, n)
|
genBreak(c, n)
|
||||||
of nkTryStmt: genTry(c, n, dest)
|
of nkTryStmt, nkHiddenTryStmt: genTry(c, n, dest)
|
||||||
of nkStmtList:
|
of nkStmtList:
|
||||||
#unused(c, n, dest)
|
#unused(c, n, dest)
|
||||||
# XXX Fix this bug properly, lexim triggers it
|
# XXX Fix this bug properly, lexim triggers it
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ type
|
||||||
wBoundchecks, wOverflowchecks, wNilchecks,
|
wBoundchecks, wOverflowchecks, wNilchecks,
|
||||||
wFloatchecks, wNanChecks, wInfChecks, wMoveChecks,
|
wFloatchecks, wNanChecks, wInfChecks, wMoveChecks,
|
||||||
wNonReloadable, wExecuteOnReload,
|
wNonReloadable, wExecuteOnReload,
|
||||||
wAssertions, wPatterns, wWarnings,
|
wAssertions, wPatterns, wTrMacros, wWarnings,
|
||||||
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
|
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
|
||||||
wDeadCodeElimUnused, # deprecated, dead code elim always happens
|
wDeadCodeElimUnused, # deprecated, dead code elim always happens
|
||||||
wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
|
wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
|
||||||
|
|
@ -145,7 +145,7 @@ const
|
||||||
"floatchecks", "nanchecks", "infchecks", "movechecks",
|
"floatchecks", "nanchecks", "infchecks", "movechecks",
|
||||||
"nonreloadable", "executeonreload",
|
"nonreloadable", "executeonreload",
|
||||||
|
|
||||||
"assertions", "patterns", "warnings", "hints",
|
"assertions", "patterns", "trmacros", "warnings", "hints",
|
||||||
"optimization", "raises", "writes", "reads", "size", "effects", "tags",
|
"optimization", "raises", "writes", "reads", "size", "effects", "tags",
|
||||||
"deadcodeelim", # deprecated, dead code elim always happens
|
"deadcodeelim", # deprecated, dead code elim always happens
|
||||||
"safecode", "package", "noforward", "reorder", "norewrite",
|
"safecode", "package", "noforward", "reorder", "norewrite",
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ proc returnsNewExpr*(n: PNode): NewLocation =
|
||||||
nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
|
nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
|
||||||
result = returnsNewExpr(n.lastSon)
|
result = returnsNewExpr(n.lastSon)
|
||||||
of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure,
|
of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure,
|
||||||
nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt:
|
nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt, nkHiddenTryStmt:
|
||||||
result = newLit
|
result = newLit
|
||||||
for i in ord(n.kind == nkObjConstr) ..< n.len:
|
for i in ord(n.kind == nkObjConstr) ..< n.len:
|
||||||
let x = returnsNewExpr(n.sons[i])
|
let x = returnsNewExpr(n.sons[i])
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ Advanced options:
|
||||||
--tlsEmulation:on|off turn thread local storage emulation on|off
|
--tlsEmulation:on|off turn thread local storage emulation on|off
|
||||||
--taintMode:on|off turn taint mode on|off
|
--taintMode:on|off turn taint mode on|off
|
||||||
--implicitStatic:on|off turn implicit compile time evaluation 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
|
--multimethods:on|off turn multi-methods on|off
|
||||||
--memTracker:on|off turn memory tracker on|off
|
--memTracker:on|off turn memory tracker on|off
|
||||||
--hotCodeReloading:on|off
|
--hotCodeReloading:on|off
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ type
|
||||||
nnkEnumTy,
|
nnkEnumTy,
|
||||||
nnkEnumFieldDef,
|
nnkEnumFieldDef,
|
||||||
nnkArglist, nnkPattern
|
nnkArglist, nnkPattern
|
||||||
nnkReturnToken,
|
nnkHiddenTryStmt,
|
||||||
nnkClosure,
|
nnkClosure,
|
||||||
nnkGotoState,
|
nnkGotoState,
|
||||||
nnkState,
|
nnkState,
|
||||||
|
|
|
||||||
|
|
@ -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
|
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
|
at offset 0 then. The ``ref`` object header is independent from the
|
||||||
runtime type and only contains a reference count.
|
runtime type and only contains a reference count.
|
||||||
|
|
@ -48,9 +48,18 @@ template `-!`(p: pointer, s: int): pointer =
|
||||||
template head(p: pointer): ptr RefHeader =
|
template head(p: pointer): ptr RefHeader =
|
||||||
cast[ptr RefHeader](cast[int](p) -% sizeof(RefHeader))
|
cast[ptr RefHeader](cast[int](p) -% sizeof(RefHeader))
|
||||||
|
|
||||||
|
var allocs*: int
|
||||||
|
|
||||||
proc nimNewObj(size: int): pointer {.compilerRtl.} =
|
proc nimNewObj(size: int): pointer {.compilerRtl.} =
|
||||||
result = alloc0(size + sizeof(RefHeader)) +! sizeof(RefHeader)
|
let s = size + sizeof(RefHeader)
|
||||||
# XXX Respect defined(useMalloc) here!
|
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.} =
|
proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
|
||||||
dec head(p).rc
|
dec head(p).rc
|
||||||
|
|
@ -59,10 +68,19 @@ proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
|
||||||
inc head(p).rc
|
inc head(p).rc
|
||||||
|
|
||||||
proc nimRawDispose(p: pointer) {.compilerRtl.} =
|
proc nimRawDispose(p: pointer) {.compilerRtl.} =
|
||||||
|
when not defined(nimscript):
|
||||||
if head(p).rc != 0:
|
if head(p).rc != 0:
|
||||||
cstderr.rawWrite "[FATAL] dangling references exist\n"
|
cstderr.rawWrite "[FATAL] dangling references exist\n"
|
||||||
quit 1
|
quit 1
|
||||||
|
when defined(useMalloc):
|
||||||
|
c_free(p -! sizeof(RefHeader))
|
||||||
|
else:
|
||||||
dealloc(p -! sizeof(RefHeader))
|
dealloc(p -! sizeof(RefHeader))
|
||||||
|
if allocs > 0:
|
||||||
|
dec allocs
|
||||||
|
else:
|
||||||
|
cstderr.rawWrite "[FATAL] unpaired dealloc\n"
|
||||||
|
quit 1
|
||||||
|
|
||||||
proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
|
proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
|
||||||
let d = cast[ptr PNimType](p)[].destructor
|
let d = cast[ptr PNimType](p)[].destructor
|
||||||
|
|
|
||||||
|
|
@ -2950,7 +2950,7 @@ when not defined(js) and not defined(nimscript):
|
||||||
when not declared(sysFatal):
|
when not declared(sysFatal):
|
||||||
include "system/fatal"
|
include "system/fatal"
|
||||||
|
|
||||||
when defined(nimV2) and not defined(nimscript):
|
when defined(nimV2):
|
||||||
include core/runtime_v2
|
include core/runtime_v2
|
||||||
|
|
||||||
import system/assertions
|
import system/assertions
|
||||||
|
|
|
||||||
15
tests/destructor/tinvalid_rebind.nim
Normal file
15
tests/destructor/tinvalid_rebind.nim
Normal file
|
|
@ -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
|
||||||
|
|
@ -22,6 +22,7 @@ var
|
||||||
|
|
||||||
proc `=destroy`*[T](x: var opt[T]) =
|
proc `=destroy`*[T](x: var opt[T]) =
|
||||||
if x.data != nil:
|
if x.data != nil:
|
||||||
|
mixin `=destroy`
|
||||||
when not supportsCopyMem(T):
|
when not supportsCopyMem(T):
|
||||||
`=destroy`(x.data[])
|
`=destroy`(x.data[])
|
||||||
dealloc(x.data)
|
dealloc(x.data)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue