diff --git a/compiler/semdata.nim b/compiler/semdata.nim index ce91fb8afc..4981b64c99 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -41,7 +41,8 @@ type inst*: PInstantiation TExprFlag* = enum - efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType + efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, + efAllowDestructor TExprFlags* = set[TExprFlag] PContext* = ref TContext diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 7bd7321615..962272ec41 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -31,6 +31,12 @@ proc semProcvarCheck(c: PContext, n: PNode) = skConverter}: performProcvarCheck(c, n, n.sym) +proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = + if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}: + if instantiateDestructor(c, n.typ): + LocalError(n.info, errGenerated, + "usage of a type with a destructor in a non destructible context") + proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags) @@ -56,6 +62,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # XXX tyGenericInst here? semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) + semDestructorCheck(c, result, flags) else: LocalError(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) @@ -72,6 +79,7 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = errorType(c) else: semProcvarCheck(c, result) + semDestructorCheck(c, result, flags) proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) @@ -390,13 +398,13 @@ proc arrayConstrType(c: PContext, n: PNode): PType = typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info) result = typ -proc semArrayConstr(c: PContext, n: PNode): PNode = +proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newNodeI(nkBracket, n.info) result.typ = newTypeS(tyArrayConstr, c) rawAddSon(result.typ, nil) # index type if sonsLen(n) == 0: rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype! - else: + else: var x = n.sons[0] var lastIndex: biggestInt = 0 var indexType = getSysType(tyInt) @@ -419,7 +427,7 @@ proc semArrayConstr(c: PContext, n: PNode): PNode = localError(x.info, errInvalidOrderInArrayConstructor) x = x.sons[1] - n.sons[i] = semExprWithType(c, x) + n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor}) addSon(result, fitNode(c, typ, n.sons[i])) inc(lastIndex) addSonSkipIntLit(result.typ, typ) @@ -457,7 +465,7 @@ proc fixAbstractType(c: PContext, n: PNode) = n.sons[i] = it.sons[1] of nkBracket: # an implicitely constructed array (passed to an open array): - n.sons[i] = semArrayConstr(c, it) + n.sons[i] = semArrayConstr(c, it, {}) else: nil #if (it.typ == nil): @@ -628,7 +636,7 @@ proc semDirectCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) -proc semObjConstr(c: PContext, n: PNode): PNode +proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil checkMinSonsLen(n, 1) @@ -682,7 +690,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # has side-effects: if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) elif t != nil and t.kind == tyTypeDesc: - if n.len == 1: return semObjConstr(c, n) + if n.len == 1: return semObjConstr(c, n, flags) let destType = t.skipTypes({tyTypeDesc, tyGenericInst}) result = semConv(c, n, symFromType(destType, n.info)) return @@ -1099,11 +1107,13 @@ proc semAsgn(c: PContext, n: PNode): PNode = localError(a.info, errXCannotBeAssignedTo, renderTree(a, {renderNoComments})) else: - var - rhs = semExprWithType(c, n.sons[1]) + let lhs = n.sons[0] - if lhs.kind == nkSym and lhs.sym.kind == skResult and - lhs.sym.typ.kind == tyGenericParam: + lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult + var + rhs = semExprWithType(c, n.sons[1], + if lhsIsResult: {efAllowDestructor} else: {}) + if lhsIsResult and lhs.sym.typ.kind == tyGenericParam: if matchTypeClass(lhs.typ, rhs.typ): InternalAssert c.p.resultSym != nil lhs.typ = rhs.typ @@ -1548,21 +1558,22 @@ proc checkPar(n: PNode): TParKind = LocalError(n.sons[i].info, errNamedExprNotAllowed) return paNone -proc semTupleFieldsConstr(c: PContext, n: PNode): PNode = +proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newNodeI(nkPar, n.info) var typ = newTypeS(tyTuple, c) typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): if (n.sons[i].kind != nkExprColonExpr) or - not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): + not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): illFormedAst(n.sons[i]) var id: PIdent if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident else: id = n.sons[i].sons[0].sym.name if ContainsOrIncl(ids, id.id): localError(n.sons[i].info, errFieldInitTwice, id.s) - n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) + n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1], + flags*{efAllowDestructor}) var f = newSymS(skField, n.sons[i].sons[0], c) f.typ = skipIntLit(n.sons[i].sons[1].typ) rawAddSon(typ, f.typ) @@ -1571,15 +1582,15 @@ proc semTupleFieldsConstr(c: PContext, n: PNode): PNode = addSon(result, n.sons[i]) result.typ = typ -proc semTuplePositionsConstr(c: PContext, n: PNode): PNode = +proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we don't modify n, but compute the type: var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in countup(0, sonsLen(n) - 1): - n.sons[i] = semExprWithType(c, n.sons[i]) + n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor}) addSonSkipIntLit(typ, n.sons[i].typ) result.typ = typ -proc semObjConstr(c: PContext, n: PNode): PNode = +proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n.sons[0], nil) result = n result.typ = t @@ -1600,8 +1611,7 @@ proc semObjConstr(c: PContext, n: PNode): PNode = else: id = it.sons[0].sym.name if ContainsOrIncl(ids, id.id): localError(it.info, errFieldInitTwice, id.s) - var e = semExprWithType(c, it.sons[1]) - + var e = semExprWithType(c, it.sons[1], flags*{efAllowDestructor}) var check: PNode = nil f: PSym @@ -1830,7 +1840,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if n.len == 2: result = semConv(c, n, s) elif n.len == 1: - result = semObjConstr(c, n) + result = semObjConstr(c, n, flags) elif Contains(c.AmbiguousSymbols, s.id): LocalError(n.info, errUseQualifier, s.name.s) elif s.magic == mNone: result = semDirectOp(c, n, flags) @@ -1869,12 +1879,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkPar: case checkPar(n) of paNone: result = errorNode(c, n) - of paTuplePositions: result = semTuplePositionsConstr(c, n) - of paTupleFields: result = semTupleFieldsConstr(c, n) + of paTuplePositions: result = semTuplePositionsConstr(c, n, flags) + of paTupleFields: result = semTupleFieldsConstr(c, n, flags) of paSingle: result = semExpr(c, n.sons[0], flags) of nkCurly: result = semSetConstr(c, n) - of nkBracket: result = semArrayConstr(c, n) - of nkObjConstr: result = semObjConstr(c, n) + of nkBracket: result = semArrayConstr(c, n, flags) + of nkObjConstr: result = semObjConstr(c, n, flags) of nkLambdaKinds: result = semLambda(c, n, flags) of nkDerefExpr: result = semDeref(c, n) of nkAddr: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4c9a7f88e4..2f5620bb47 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -203,11 +203,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var typ: PType if a.sons[length-2].kind != nkEmpty: typ = semTypeNode(c, a.sons[length-2], nil) - else: + else: typ = nil var def: PNode - if a.sons[length-1].kind != nkEmpty: - def = semExprWithType(c, a.sons[length-1]) + if a.sons[length-1].kind != nkEmpty: + def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) # BUGFIX: ``fitNode`` is needed here! # check type compability between def.typ and typ: if typ != nil: def = fitNode(c, typ, def) diff --git a/tests/reject/tdestructor.nim b/tests/reject/tdestructor.nim new file mode 100644 index 0000000000..da9192a3f9 --- /dev/null +++ b/tests/reject/tdestructor.nim @@ -0,0 +1,21 @@ +discard """ + line: 20 + errormsg: " usage of a type with a destructor in a non destructible context" +""" + +type + TMyObj = object + x, y: int + p: pointer + +proc destruct(o: var TMyObj) {.destructor.} = + if o.p != nil: dealloc o.p + +proc open: TMyObj = + result = TMyObj(x: 1, y: 2, p: alloc(3)) + + +proc `$`(x: TMyObj): string = $x.y + +echo open() + diff --git a/tests/run/tdestructor.nim b/tests/run/tdestructor.nim new file mode 100644 index 0000000000..8aae2fce20 --- /dev/null +++ b/tests/run/tdestructor.nim @@ -0,0 +1,26 @@ +discard """ + output: '''some text +Destructor called!''' +""" + +type + TMyObj = object + x, y: int + p: pointer + +proc destruct(o: var TMyObj) {.destructor.} = + if o.p != nil: dealloc o.p + echo "Destructor called!" + +proc open: TMyObj = + # allow for superfluous () + result = (TMyObj(x: 1, y: 2, p: alloc(3))) + + +proc `$`(x: TMyObj): string = $x.y + +proc main() = + var x = open() + echo "some text" + +main() diff --git a/todo.txt b/todo.txt index eff9ae87ee..3940fe0582 100644 --- a/todo.txt +++ b/todo.txt @@ -1,19 +1,14 @@ version 0.9.2 ============= -- fix destructors; don't work yet when used as expression; alternative for - version 1: disallow expressions yielding a type with a destructor that are - not in a 'let/var' context (p(a.openFile, b.openFile) makes no sense anyway) -- lazy overloading resolution: - * get rid of ``expr[typ]``, use perhaps ``static[typ]`` instead - * special case ``tyStmt`` +- document destructors restrictions +- a project wide override option for 'dynlib' - FFI: * test libffi on windows * test: times.format with the FFI - acyclic vs prunable; introduce GC hints - CGEN: ``restrict`` pragma + backend support; computed goto support - document NimMain and check whether it works for threading -- a project wide override option for 'dynlib' Bugs @@ -44,6 +39,9 @@ version 0.9.4 - implicit deref for parameter matching; overloading based on 'var T' - ``=`` should be overloadable; requires specialization for ``=``; general lift mechanism in the compiler is already implemented for 'fields' +- lazy overloading resolution: + * get rid of ``expr[typ]``, use perhaps ``static[typ]`` instead + * special case ``tyStmt`` version 0.9.X