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:
Andreas Rumpf 2019-03-23 00:12:05 +01:00 • committed by GitHub
commit bf592c4e94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 443 additions and 242 deletions

View file

@ -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,

View file

@ -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:

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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))

View file

@ -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)

View file

@ -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)

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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 =

View file

@ -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}:

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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))

View file

@ -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):

View file

@ -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)

View file

@ -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:

View file

@ -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;

View file

@ -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)

View file

@ -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

View file

@ -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",

View file

@ -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])

View file

@ -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

View file

@ -78,7 +78,7 @@ type
nnkEnumTy, nnkEnumTy,
nnkEnumFieldDef, nnkEnumFieldDef,
nnkArglist, nnkPattern nnkArglist, nnkPattern
nnkReturnToken, nnkHiddenTryStmt,
nnkClosure, nnkClosure,
nnkGotoState, nnkGotoState,
nnkState, nnkState,

View file

@ -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

View file

@ -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

View 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

View file

@ -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)