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