destructors: internal compiler refactoring

This commit is contained in:
Araq 2019-04-18 00:53:02 +02:00
commit 750f50b6c0
7 changed files with 77 additions and 103 deletions

View file

@ -877,6 +877,13 @@ type
TTypeSeq* = seq[PType] TTypeSeq* = seq[PType]
TLockLevel* = distinct int16 TLockLevel* = distinct int16
TTypeAttachedOp* = enum ## as usual, order is important here
attachedDestructor,
attachedAsgn,
attachedSink,
attachedDeepCopy
TType* {.acyclic.} = object of TIdObj # \ TType* {.acyclic.} = object of TIdObj # \
# types are identical iff they have the # types are identical iff they have the
# same id; there may be multiple copies of a type # same id; there may be multiple copies of a type
@ -897,12 +904,7 @@ type
owner*: PSym # the 'owner' of the type owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them sym*: PSym # types have the sym associated with them
# it is used for converting types to strings # it is used for converting types to strings
destructor*: PSym # destructor. warning: nil here may not necessary attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc.
# mean that there is no destructor.
# see instantiateDestructor in semdestruct.nim
deepCopy*: PSym # overriden 'deepCopy' operation
assignment*: PSym # overriden '=' operation
sink*: PSym # overriden '=sink' operation
methods*: seq[(int,PSym)] # attached methods methods*: seq[(int,PSym)] # attached methods
size*: BiggestInt # the size of the type in bytes size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
@ -1275,6 +1277,7 @@ const
UnspecifiedLockLevel* = TLockLevel(-1'i16) UnspecifiedLockLevel* = TLockLevel(-1'i16)
MaxLockLevel* = 1000'i16 MaxLockLevel* = 1000'i16
UnknownLockLevel* = TLockLevel(1001'i16) UnknownLockLevel* = TLockLevel(1001'i16)
AttachedOpToStr*: array[TTypeAttachedOp, string] = ["=destroy", "=", "=sink", "=deepcopy"]
proc `$`*(x: TLockLevel): string = proc `$`*(x: TLockLevel): string =
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>" if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
@ -1341,10 +1344,7 @@ proc assignType*(dest, src: PType) =
dest.n = src.n dest.n = src.n
dest.size = src.size dest.size = src.size
dest.align = src.align dest.align = src.align
dest.destructor = src.destructor dest.attachedOps = src.attachedOps
dest.deepCopy = src.deepCopy
dest.sink = src.sink
dest.assignment = src.assignment
dest.lockLevel = src.lockLevel dest.lockLevel = src.lockLevel
# this fixes 'type TLock = TSysLock': # this fixes 'type TLock = TSysLock':
if src.sym != nil: if src.sym != nil:
@ -1831,3 +1831,7 @@ proc addParam*(procType: PType; param: PSym) =
param.position = procType.len-1 param.position = procType.len-1
addSon(procType.n, newSymNode(param)) addSon(procType.n, newSymNode(param))
rawAddSon(procType, param.typ) rawAddSon(procType, param.typ)
template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor]
template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
template asink*(t: PType): PSym = t.attachedOps[attachedSink]

View file

@ -1346,10 +1346,10 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
# results are not deterministic! # results are not deterministic!
genTupleInfo(m, t, origType, result, info) genTupleInfo(m, t, origType, result, info)
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
if t.deepCopy != nil: if t.attachedOps[attachedDeepCopy] != nil:
genDeepCopyProc(m, t.deepCopy, result) genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
elif origType.deepCopy != nil: elif origType.attachedOps[attachedDeepCopy] != nil:
genDeepCopyProc(m, origType.deepCopy, result) genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result)
result = prefixTI.rope & result & ")".rope result = prefixTI.rope & result & ")".rope
proc genTypeSection(m: BModule, n: PNode) = proc genTypeSection(m: BModule, n: PNode) =

View file

@ -136,7 +136,7 @@ to do it.
import import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
strutils, options, dfa, lowerings, tables, modulegraphs, msgs, strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
lineinfos, parampatterns lineinfos, parampatterns, sighashes
const const
InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp} InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp}
@ -314,24 +314,34 @@ proc makePtrType(c: Con, baseType: PType): PType =
result = newType(tyPtr, c.owner) result = newType(tyPtr, c.owner)
addSonSkipIntLit(result, baseType) addSonSkipIntLit(result, baseType)
template genOp(opr, opname, ri) = proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
let op = opr var op = t.attachedOps[kind]
when false:
if op == nil:
# give up and find the canonical type instead:
let h = sighashes.hashType(t, {CoType, CoConsiderOwned})
let canon = c.graph.canonTypes.getOrDefault(h)
if canon != nil:
op = canon.attachedOps[kind]
if op == nil: if op == nil:
globalError(c.graph.config, dest.info, "internal error: '" & opname & globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
"' operator not found for type " & typeToString(t)) "' operator not found for type " & typeToString(t))
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: '" & AttachedOpToStr[kind] &
"' operator is generic") "' operator is generic")
if sfError in op.flags: checkForErrorPragma(c, t, ri, opname) if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
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)
result = newTree(nkCall, newSymNode(op), addrExp) result = newTree(nkCall, newSymNode(op), addrExp)
proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
let op = if t.sink != nil: t.sink else: t.assignment let k = if t.attachedOps[attachedSink] != nil: attachedSink
if op != nil: else: attachedAsgn
genOp(op, "=sink", ri) if t.attachedOps[k] != nil:
result = genOp(c, t, k, dest, ri)
else: else:
# in rare cases only =destroy exists but no sink or assignment # in rare cases only =destroy exists but no sink or assignment
# (see Pony object in tmove_objconstr.nim) # (see Pony object in tmove_objconstr.nim)
@ -342,15 +352,15 @@ proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode =
if tfHasOwned in t.flags: if tfHasOwned in t.flags:
checkForErrorPragma(c, t, ri, "=") checkForErrorPragma(c, t, ri, "=")
let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(t.assignment, "=", ri) result = genOp(c, t, attachedAsgn, dest, ri)
proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode = proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(t.assignment, "=", ri) result = genOp(c, t, attachedAsgn, dest, ri)
proc genDestroy(c: Con; t: PType; dest: PNode): PNode = proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(t.destructor, "=destroy", nil) result = genOp(c, t, attachedDestructor, dest, nil)
proc addTopVar(c: var Con; v: PNode) = proc addTopVar(c: var Con; v: PNode) =
c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode) c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)

View file

@ -197,7 +197,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
assert t.typeInst != nil assert t.typeInst != nil
# patch generic destructor: # patch generic destructor:
op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1) op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
t.destructor = op t.attachedOps[attachedDestructor] = op
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)
@ -207,9 +207,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
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:
result = considerAsgnOrSink(c, t, body, x, y, t.sink) result = considerAsgnOrSink(c, t, body, x, y, t.asink)
of attachedDeepCopy: of attachedDeepCopy:
let op = t.deepCopy let op = t.attachedOps[attachedDeepCopy]
if op != nil: if op != nil:
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)
@ -323,8 +323,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add moveCall body.add moveCall
# alternatively we could do this: # alternatively we could do this:
when false: when false:
doAssert t.sink != nil doAssert t.asink != nil
body.add newAsgnCall(c.graph, t.sink, x, y) body.add newAsgnCall(c.graph, t.asink, x, y)
of attachedDestructor: of attachedDestructor:
doAssert t.destructor != nil doAssert t.destructor != nil
body.add destructorCall(c.graph, t.destructor, x) body.add destructorCall(c.graph, t.destructor, x)
@ -502,27 +502,10 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = proc produceSymDistinctType(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 if baseType.attachedOps[kind] == nil:
of attachedAsgn: discard produceSym(c, baseType, kind, info)
if baseType.assignment == nil: typ.attachedOps[kind] = baseType.attachedOps[kind]
discard produceSym(c, baseType, kind, info) result = typ.attachedOps[kind]
typ.assignment = baseType.assignment
result = typ.assignment
of attachedSink:
if baseType.sink == nil:
discard produceSym(c, baseType, kind, info)
typ.sink = baseType.sink
result = typ.sink
of attachedDeepCopy:
if baseType.deepCopy == nil:
discard produceSym(c, baseType, kind, info)
typ.deepCopy = baseType.deepCopy
result = typ.deepCopy
of attachedDestructor:
if baseType.destructor == nil:
discard produceSym(c, baseType, kind, info)
typ.destructor = baseType.destructor
result = typ.destructor
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp; proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym = info: TLineInfo): PSym =
@ -536,11 +519,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
a.c = c a.c = c
let g = c.graph let g = c.graph
let body = newNodeI(nkStmtList, info) let body = newNodeI(nkStmtList, info)
let procname = case kind let procname = getIdent(g.cache, AttachedOpToStr[kind])
of attachedAsgn: getIdent(g.cache, "=")
of attachedSink: getIdent(g.cache, "=sink")
of attachedDeepCopy: getIdent(g.cache, "=deepcopy")
of attachedDestructor: getIdent(g.cache, "=destroy")
result = newSym(skProc, procname, typ.owner, info) result = newSym(skProc, procname, typ.owner, info)
a.fn = result a.fn = result
@ -557,11 +536,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
result.typ.addParam src result.typ.addParam src
# register this operation already: # register this operation already:
case kind typ.attachedOps[kind] = result
of attachedAsgn: typ.assignment = result
of attachedSink: typ.sink = result
of attachedDeepCopy: typ.deepCopy = result
of attachedDestructor: typ.destructor = result
var tk: TTypeKind var tk: TTypeKind
if optNimV2 in c.graph.config.globalOptions: if optNimV2 in c.graph.config.globalOptions:
@ -636,23 +611,15 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
# 5. We have a (custom) generic destructor. # 5. We have a (custom) generic destructor.
let typ = canon.skipTypes({tyGenericInst, tyAlias}) let typ = canon.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: for k in attachedDestructor..attachedSink:
discard produceSym(c, typ, attachedDestructor, info) if typ.attachedOps[k] == nil:
else: discard produceSym(c, typ, k, info)
inst(typ.destructor, typ) else:
if typ.assignment == nil: inst(typ.attachedOps[k], typ)
discard produceSym(c, typ, attachedAsgn, info)
else:
inst(typ.assignment, typ)
if typ.sink == nil:
discard produceSym(c, typ, attachedSink, info)
else:
inst(typ.sink, typ)
if overwrite: if overwrite:
orig.destructor = typ.destructor for k in attachedDestructor..attachedSink:
orig.assignment = typ.assignment orig.attachedOps[k] = typ.attachedOps[k]
orig.sink = typ.sink
if not isTrival(orig.destructor): if not isTrival(orig.destructor):
#or not isTrival(orig.assignment) or #or not isTrival(orig.assignment) or

View file

@ -72,12 +72,6 @@ type
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
TTypeAttachedOp* = enum
attachedAsgn,
attachedSink,
attachedDeepCopy,
attachedDestructor
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module TContext* = object of TPassContext # a context represents a module
enforceVoidContext*: PType enforceVoidContext*: PType

View file

@ -1568,7 +1568,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
obj = canonType(c, obj) obj = canonType(c, obj)
if obj.destructor.isNil: if obj.destructor.isNil:
obj.destructor = s obj.attachedOps[attachedDestructor] = s
else: else:
prevDestructor(c, obj.destructor, obj, n.info) prevDestructor(c, obj.destructor, obj, n.info)
noError = true noError = true
@ -1592,7 +1592,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
elif t.kind == tyGenericInvocation: t = t.sons[0] elif t.kind == tyGenericInvocation: t = t.sons[0]
else: break else: break
if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
if t.deepCopy.isNil: t.deepCopy = s if t.attachedOps[attachedDeepCopy].isNil: t.attachedOps[attachedDeepCopy] = s
else: else:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"cannot bind another 'deepCopy' to: " & typeToString(t)) "cannot bind another 'deepCopy' to: " & typeToString(t))
@ -1631,11 +1631,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
# attach these ops to the canonical tySequence # attach these ops to the canonical tySequence
obj = canonType(c, obj) obj = canonType(c, obj)
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](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) let k = if name == "=": attachedAsgn else: attachedSink
if opr[].isNil: if obj.attachedOps[k].isNil:
opr[] = s obj.attachedOps[k] = s
else: else:
prevDestructor(c, opr[], obj, n.info) prevDestructor(c, obj.attachedOps[k], obj, n.info)
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() & ")")
@ -1828,7 +1828,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}: if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
localError(c.config, n.info, "the overloaded " & s.name.s & localError(c.config, n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"dotOperators\".}") " operator has to be enabled with {.experimental: \"dotOperators\".}")
elif s.name.s == "()" and callOperator notin c.features: elif s.name.s == "()" and callOperator notin c.features:

View file

@ -380,13 +380,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
rawAddSon(result, newbody) rawAddSon(result, newbody)
checkPartialConstructedType(cl.c.config, cl.info, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody)
let dc = newbody.deepCopy let dc = newbody.attachedOps[attachedDeepCopy]
if not cl.allowMetaTypes: if not cl.allowMetaTypes:
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: if dc != nil and sfFromGeneric notin newbody.attachedOps[attachedDeepCopy].flags:
# 'deepCopy' needs to be instantiated for # 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*: # generics *when the type is constructed*:
newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
attachedDeepCopy, 1) attachedDeepCopy, 1)
if bodyIsNew and newbody.typeInst == nil: if bodyIsNew and newbody.typeInst == nil:
#doassert newbody.typeInst == nil #doassert newbody.typeInst == nil
newbody.typeInst = result newbody.typeInst = result
@ -592,7 +592,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
skipIntLiteralParams(result) skipIntLiteralParams(result)
of tySequence: of tySequence:
if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and result.destructor.isNil and if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and
result.attachedOps[attachedDestructor].isNil and
result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions: result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions:
let s = cl.c.graph.sysTypes[tySequence] let s = cl.c.graph.sysTypes[tySequence]
var old = copyType(s, s.owner, keepId=false) var old = copyType(s, s.owner, keepId=false)
@ -601,9 +602,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
old.n = nil old.n = nil
old.flags = {tfHasAsgn} old.flags = {tfHasAsgn}
old.addSonSkipIntLit result[0] old.addSonSkipIntLit result[0]
result.destructor = old.destructor result.attachedOps = old.attachedOps
result.assignment = old.assignment
result.sink = old.sink
cl.c.typesWithOps.add((result, old)) cl.c.typesWithOps.add((result, old))
else: discard else: discard
@ -619,19 +618,19 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result.n = replaceObjBranches(cl, result.n) result.n = replaceObjBranches(cl, result.n)
template typeBound(c, newty, oldty, field, info) = template typeBound(c, newty, oldty, field, info) =
let opr = newty.field let opr = newty.attachedOps[field]
if opr != nil and sfFromGeneric notin opr.flags: if opr != nil and sfFromGeneric notin opr.flags:
# '=' needs to be instantiated for generics when the type is constructed: # '=' needs to be instantiated for generics when the type is constructed:
#echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty) #echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty)
newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1) newty.attachedOps[field] = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) = proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
var i = 0 var i = 0
while i < c.typesWithOps.len: while i < c.typesWithOps.len:
let (newty, oldty) = c.typesWithOps[i] let (newty, oldty) = c.typesWithOps[i]
typeBound(c, newty, oldty, destructor, info) typeBound(c, newty, oldty, attachedDestructor, info)
typeBound(c, newty, oldty, sink, info) typeBound(c, newty, oldty, attachedSink, info)
typeBound(c, newty, oldty, assignment, info) typeBound(c, newty, oldty, attachedAsgn, info)
inc i inc i
setLen(c.typesWithOps, 0) setLen(c.typesWithOps, 0)