gc: destructors is beginning to work (#10483)

* kochdocs.nim: code cleanup
* docgen: nicer indentation
* parser.nim: code cleanup
* fixes #10458
* make tests green again
* make =destroy mixins
* gc:destructors: produced C code is almost working
* --gc:destructors simple program compiles (but leaks memory)
* gc:destructors make examples compile in C++ mode
* destructors: string implementation bugfixes
* strs.nim: minor code cleanup
* destructors: builtin seqs are beginning to work
* remove debugging helpers
This commit is contained in:
Andreas Rumpf 2019-01-29 15:12:16 +01:00 • committed by GitHub
commit dee8e6e98a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 162 additions and 101 deletions

View file

@ -1352,7 +1352,6 @@ proc copySym*(s: PSym): PSym =
result = newSym(s.kind, s.name, s.owner, s.info, s.options)
#result.ast = nil # BUGFIX; was: s.ast which made problems
result.typ = s.typ
result.id = getID()
when debugIds: registerId(result)
result.flags = s.flags
result.magic = s.magic

View file

@ -460,7 +460,7 @@ proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr")
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
lineCg(p, cpsStmts, frmt, addrLoc(p.config, a), rdLoc(b))
lineCg(p, cpsStmts, frmt, byRefLoc(p, a), rdLoc(b))
proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a: TLoc
@ -1028,7 +1028,7 @@ proc gcUsage(conf: ConfigRef; n: PNode) =
proc strLoc(p: BProc; d: TLoc): Rope =
if p.config.selectedGc == gcDestructors:
result = addrLoc(p.config, d)
result = byRefLoc(p, d)
else:
result = rdLoc(d)
@ -1110,7 +1110,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
strLoc(p, dest), rdLoc(a)))
if p.config.selectedGC == gcDestructors:
linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
addrLoc(p.config, dest), lens, rope(L))
byRefLoc(p, dest), lens, rope(L))
else:
initLoc(call, locCall, e, OnHeap)
call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])

View file

@ -264,6 +264,12 @@ proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray:
result = "(&" & result & ")"
proc byRefLoc(p: BProc; a: TLoc): Rope =
result = a.r
if lfIndirect notin a.flags and mapType(p.config, a.t) != ctArray and not
p.module.compileToCpp:
result = "(&" & result & ")"
proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast:
result = rdLoc(a)

View file

@ -244,7 +244,10 @@ proc patchHead(n: PNode) =
proc patchHead(s: PSym) =
if sfFromGeneric in s.flags:
patchHead(s.ast[bodyPos])
# do not patch the builtin type bound operators for seqs:
let dest = s.typ.sons[1].skipTypes(abstractVar)
if dest.kind != tySequence:
patchHead(s.ast[bodyPos])
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
@ -267,7 +270,8 @@ template genOp(opr, opname, ri) =
globalError(c.graph.config, dest.info, "internal error: '" & opname &
"' operator not found for type " & typeToString(t))
elif op.ast[genericParamsPos].kind != nkEmpty:
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
globalError(c.graph.config, dest.info, "internal error: '" & opname &
"' operator is generic")
patchHead op
if sfError in op.flags: checkForErrorPragma(c, t, ri, opname)
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
@ -275,6 +279,12 @@ template genOp(opr, opname, ri) =
result = newTree(nkCall, newSymNode(op), addrExp)
proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
when false:
if t.kind != tyString:
echo "this one ", c.graph.config$dest.info, " for ", typeToString(t, preferDesc)
debug t.sink.typ.sons[2]
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)

View file

@ -724,6 +724,14 @@ const
tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType,
tkEnum, tkTuple, tkObject, tkProc}
proc commandExpr(p: var TParser; r: PNode; mode: TPrimaryMode): PNode =
result = newNodeP(nkCommand, p)
addSon(result, r)
var isFirstParam = true
# progress NOT guaranteed
p.hasProgress = false
addSon result, commandParam(p, isFirstParam, mode)
proc primarySuffix(p: var TParser, r: PNode,
baseIndent: int, mode: TPrimaryMode): PNode =
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
@ -734,8 +742,6 @@ proc primarySuffix(p: var TParser, r: PNode,
#| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
result = r
template somePar() =
if p.tok.strongSpaceA > 0: break
# progress guaranteed
while p.tok.indent < 0 or
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
@ -749,6 +755,8 @@ proc primarySuffix(p: var TParser, r: PNode,
result = newNodeP(nkCommand, p)
result.addSon r
result.addSon primary(p, pmNormal)
else:
result = commandExpr(p, result, mode)
break
result = namedParams(p, result, nkCall, tkParRi)
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
@ -759,39 +767,27 @@ proc primarySuffix(p: var TParser, r: PNode,
result = parseGStrLit(p, result)
of tkBracketLe:
# progress guaranteed
somePar()
if p.tok.strongSpaceA > 0:
result = commandExpr(p, result, mode)
break
result = namedParams(p, result, nkBracketExpr, tkBracketRi)
of tkCurlyLe:
# progress guaranteed
somePar()
if p.tok.strongSpaceA > 0:
result = commandExpr(p, result, mode)
break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}:
# XXX: In type sections we allow the free application of the
# command syntax, with the exception of expressions such as
# `foo ref` or `foo ptr`. Unfortunately, these two are also
# used as infix operators for the memory regions feature and
# the current parsing rules don't play well here.
# XXX: In type sections we allow the free application of the
# command syntax, with the exception of expressions such as
# `foo ref` or `foo ptr`. Unfortunately, these two are also
# used as infix operators for the memory regions feature and
# the current parsing rules don't play well here.
if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
# solution, but pragmas.nim can't handle that
let a = result
result = newNodeP(nkCommand, p)
addSon(result, a)
var isFirstParam = true
when true:
# progress NOT guaranteed
p.hasProgress = false
addSon result, commandParam(p, isFirstParam, mode)
if not p.hasProgress: break
else:
while p.tok.tokType != tkEof:
let x = parseExpr(p)
addSon(result, x)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, x)
result = postExprBlocks(p, result)
result = commandExpr(p, result, mode)
break
else:
break

View file

@ -316,6 +316,11 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym =
if typ.kind == tyDistinct:
return liftBodyDistinctType(g, typ, kind, info)
when false:
var typ = typ
if c.config.selectedGC == gcDestructors and typ.kind == tySequence:
# use the canonical type to access the =sink and =destroy etc.
typ = c.graph.sysTypes[tySequence]
var a: TLiftCtx
a.info = info

View file

@ -168,7 +168,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
else: illFormedAst(it, c.config)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
(not hasElse and efInTypeof notin flags):
for it in n:
for it in n:
it.sons[^1] = discardCheck(c, it.sons[^1], flags)
result.kind = nkIfStmt
# propagate any enforced VoidContext:
@ -1563,6 +1563,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
# attach these ops to the canonical tySequence
obj = canonType(c, obj)
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
if opr[].isNil:
opr[] = s

View file

@ -1159,7 +1159,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
# compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
if kind notin {skMacro, skTemplate} and r.kind in {tyStmt, tyExpr}:
localError(c.config, n.sons[0].info, "return type '" & typeToString(r) &
localError(c.config, n.sons[0].info, "return type '" & typeToString(r) &
"' is only valid for macros and templates")
# 'auto' as a return type does not imply a generic:
elif r.kind == tyAnything:
@ -1577,11 +1577,16 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
assert s != nil
assert prev == nil
result = copyType(s, s.owner, keepId=false)
# XXX figure out why this has children already...
# Remove the 'T' parameter from tySequence:
result.sons.setLen 0
result.n = nil
result.flags = {tfHasAsgn}
semContainerArg(c, n, "seq", result)
if result.len > 0:
var base = result[0]
if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
if base.kind != tyGenericParam:
c.typesWithOps.add((result, result))
else:
result = semContainer(c, n, tySequence, "seq", prev)
if c.config.selectedGc == gcDestructors:
@ -1714,11 +1719,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c)
n.typ = result
dec c.inTypeContext
if c.inTypeContext == 0: instAllTypeBoundOp(c, n.info)
when false:
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semTypeNodeInner(c, n, prev)
if c.inTypeContext == 0:
#if $n == "var seq[StackTraceEntry]":
# echo "begin ", n
instAllTypeBoundOp(c, n.info)
proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =

View file

@ -297,12 +297,6 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
#result.destructor = nil
result.sink = nil
template typeBound(c, newty, oldty, field, info) =
let opr = newty.field
if opr != nil and sfFromGeneric notin opr.flags:
# '=' needs to be instantiated for generics when the type is constructed:
newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# tyGenericInvocation[A, tyGenericInvocation[A, B]]
# is difficult to handle:
@ -317,7 +311,10 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
else:
result = searchInstTypes(t)
if result != nil and eqFlags*result.flags == eqFlags*t.flags: return
if result != nil and eqFlags*result.flags == eqFlags*t.flags:
when defined(reportCacheHits):
echo "Generic instantiation cached ", typeToString(result), " for ", typeToString(t)
return
for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i]
if x.kind in {tyGenericParam}:
@ -332,7 +329,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if header != t:
# search again after first pass:
result = searchInstTypes(header)
if result != nil and eqFlags*result.flags == eqFlags*t.flags: return
if result != nil and eqFlags*result.flags == eqFlags*t.flags:
when defined(reportCacheHits):
echo "Generic instantiation cached ", typeToString(result), " for ",
typeToString(t), " header ", typeToString(header)
return
else:
header = instCopyType(cl, t)
@ -384,7 +385,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
rawAddSon(result, newbody)
checkPartialConstructedType(cl.c.config, cl.info, newbody)
let dc = newbody.deepCopy
if cl.allowMetaTypes == false:
if not cl.allowMetaTypes:
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*:
@ -402,6 +403,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
discard
else:
newbody.lastSon.typeInst = result
# DESTROY: adding object|opt for opt[topttree.Tree]
# sigmatch: Formal opt[=destroy.T] real opt[topttree.Tree]
# 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))
let mm = skipTypes(bbody, abstractPtrs)
if tfFromGeneric notin mm.flags:
@ -432,7 +438,7 @@ proc eraseVoidParams*(t: PType) =
inc pos
setLen t.sons, pos
setLen t.n.sons, pos
return
break
proc skipIntLiteralParams*(t: PType) =
for i in 0 ..< t.sonsLen:
@ -561,9 +567,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
for i in countup(0, sonsLen(result) - 1):
if result.sons[i] != nil:
if result.sons[i].kind == tyGenericBody:
localError(
cl.c.config,
t.sym.info,
localError(cl.c.config, t.sym.info,
"cannot instantiate '" &
typeToString(result.sons[i], preferDesc) &
"' inside of type definition: '" &
@ -603,6 +607,13 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result.size = -1
result.n = replaceObjBranches(cl, result.n)
template typeBound(c, newty, oldty, field, info) =
let opr = newty.field
if opr != nil and sfFromGeneric notin opr.flags:
# '=' needs to be instantiated for generics when the type is constructed:
#echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty)
newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
var i = 0
while i < c.typesWithOps.len:

View file

@ -2505,6 +2505,11 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
if f.kind in {tyRef, tyPtr}: f = f.lastSon
else:
if f.kind == tyVar: f = f.lastSon
#if c.config.selectedGC == gcDestructors and f.kind == tySequence:
# use the canonical type to access the =sink and =destroy etc.
# f = c.graph.sysTypes[tySequence]
#echo "YUP_---------Formal ", typeToString(f, preferDesc), " real ", typeToString(t, preferDesc), " ", f.id, " ", t.id
if typeRel(m, f, t) == isNone:
localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
else: