diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6d5f255670c1..13e075bf4bda 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2172,7 +2172,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = genRepr(p, e, d) of mOf: genOf(p, e, d) of mNew: genNew(p, e) - of mNewFinalize: genNewFinalize(p, e) + of mNewFinalize: + if optTinyRtti in p.config.globalOptions: + genNew(p, e) + else: + genNewFinalize(p, e) of mNewSeq: genNewSeq(p, e) of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: diff --git a/compiler/nim.cfg b/compiler/nim.cfg index ba90a8284ac4..05f4ed5e9d40 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -1,6 +1,7 @@ # Special configuration file for the Nim project hint[XDeclaredButNotUsed]:off +hint[Link]:off define:booting define:nimcore diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 99053809642a..c259f0d7c6bb 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -383,6 +383,35 @@ proc semUnown(c: PContext; n: PNode): PNode = # little hack for injectdestructors.nim (see bug #11350): #result[0].typ = nil +proc turnFinalizerIntoDestructor(c: PContext; orig: PSym): PSym = + # We need to do 2 things: Replace n.typ which is a 'ref T' by a 'var T' type. + # Replace nkDerefExpr by nkHiddenDeref + # nkDeref is for 'ref T': x[].field + # nkHiddenDeref is for 'var T': x.field + proc transform(n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode = + result = shallowCopy(n) + if sameTypeOrNil(n.typ, old): + result.typ = fresh + if n.kind == nkSym and n.sym == oldParam: + result.sym = newParam + for i in 0 ..< safeLen(n): + result[i] = transform(n[i], old, fresh, oldParam, newParam) + #if n.kind == nkDerefExpr and sameType(n[0].typ, old): + # result = + + result = copySym(orig) + result.flags.incl sfFromGeneric + let origParamType = orig.typ[1] + let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs)) + let oldParam = orig.typ.n[1].sym + let newParam = newSym(skParam, oldParam.name, result, result.info) + newParam.typ = newParamType + # proc body: + result.ast = transform(orig.ast, origParamType, newParamType, oldParam, newParam) + # proc signature: + result.typ = newProcType(result.info, result) + result.typ.addParam newParam + proc magicsAfterOverloadResolution(c: PContext, n: PNode, flags: TExprFlags): PNode = ## This is the preferred code point to implement magics. @@ -447,6 +476,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, # Make sure the finalizer procedure refers to a procedure 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 proc") + elif optTinyRtti in c.config.globalOptions: + bindTypeHook(c, turnFinalizerIntoDestructor(c, n[^1].sym), n, attachedDestructor) result = n of mDestroy: result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b2837c6126f3..1b9c388b3ba5 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -758,6 +758,7 @@ proc track(tracked: PEffects, n: PNode) = if n[1].typ.len > 0: createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) createTypeBoundOps(tracked, n[1].typ, n.info) + # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and tracked.owner.kind != skMacro: diff --git a/lib/system.nim b/lib/system.nim index 838205c57f51..b9f0cdd335af 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -250,7 +250,7 @@ const ThisIsSystem = true proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} ## Leaked implementation detail. Do not use. -when not defined(gcDestructors): +when true: proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {. magic: "NewFinalize", noSideEffect.} ## Creates a new object of type ``T`` and returns a safe (traced) @@ -2198,17 +2198,18 @@ proc insert*[T](x: var seq[T], item: T, i = 0.Natural) {.noSideEffect.} = defaultImpl() x[i] = item -proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} - ## Takes any Nim variable and returns its string representation. - ## - ## It works even for complex data graphs with cycles. This is a great - ## debugging tool. - ## - ## .. code-block:: Nim - ## var s: seq[string] = @["test2", "test2"] - ## var i = @[1, 2, 3, 4, 5] - ## echo repr(s) # => 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"] - ## echo repr(i) # => 0x1055ed050[1, 2, 3, 4, 5] +when not defined(nimV2): + proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} + ## Takes any Nim variable and returns its string representation. + ## + ## It works even for complex data graphs with cycles. This is a great + ## debugging tool. + ## + ## .. code-block:: Nim + ## var s: seq[string] = @["test2", "test2"] + ## var i = @[1, 2, 3, 4, 5] + ## echo repr(s) # => 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"] + ## echo repr(i) # => 0x1055ed050[1, 2, 3, 4, 5] type ByteAddress* = int @@ -3554,6 +3555,9 @@ template unlikely*(val: bool): bool = import system/dollars export dollars +when defined(nimV2): + import system/repr_v2 + export repr_v2 const NimMajor* {.intdefine.}: int = 1 @@ -4200,6 +4204,9 @@ type NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj ## Represents a Nim AST node. Macros operate on this type. +when defined(nimV2): + proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.} + macro lenVarargs*(x: varargs[untyped]): int {.since: (1, 1).} = ## returns number of variadic arguments in `x` proc lenVarargsImpl(x: NimNode): NimNode {.magic: "LengthOpenArray", noSideEffect.} diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim new file mode 100644 index 000000000000..5f4e7ff87d7c --- /dev/null +++ b/lib/system/repr_v2.nim @@ -0,0 +1,170 @@ +proc repr*(x: int): string {.magic: "IntToStr", noSideEffect.} + ## repr for an integer argument. Returns `x` + ## converted to a decimal string. + +proc repr*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} + ## repr for an integer argument. Returns `x` + ## converted to a decimal string. + +proc repr*(x: float): string {.magic: "FloatToStr", noSideEffect.} + ## repr for a float argument. Returns `x` + ## converted to a decimal string. + +proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.} + ## repr for a boolean argument. Returns `x` + ## converted to the string "false" or "true". + +proc repr*(x: char): string {.magic: "CharToStr", noSideEffect.} + ## repr for a character argument. Returns `x` + ## converted to a string. + ## + ## .. code-block:: Nim + ## assert $'c' == "c" + +proc repr*(x: cstring): string {.magic: "CStrToStr", noSideEffect.} + ## repr for a CString argument. Returns `x` + ## converted to a string. + +proc repr*(x: string): string {.magic: "StrToStr", noSideEffect.} + ## repr for a string argument. Returns `x` + ## as it is. This operator is useful for generic code, so + ## that ``$expr`` also works if ``expr`` is already a string. + +proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.} + ## repr for an enumeration argument. This works for + ## any enumeration type thanks to compiler magic. + ## + ## If a `repr` operator for a concrete enumeration is provided, this is + ## used instead. (In other words: *Overwriting* is possible.) + +template repr(t: typedesc): string = $t + +proc isNamedTuple(T: typedesc): bool = + # Taken from typetraits. + when T isnot tuple: result = false + else: + var t: T + for name, _ in t.fieldPairs: + when name == "Field0": + return compiles(t.Field0) + else: + return true + return false + +proc repr*[T: tuple|object](x: T): string = + ## Generic `repr` operator for tuples that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: Nim + ## $(23, 45) == "(23, 45)" + ## $(a: 23, b: 45) == "(a: 23, b: 45)" + ## $() == "()" + when T is object: + result = $typeof(x) + else: + result = "" + result.add '(' + var firstElement = true + const isNamed = T is object or isNamedTuple(T) + when not isNamed: + var count = 0 + for name, value in fieldPairs(x): + if not firstElement: result.add(", ") + when isNamed: + result.add(name) + result.add(": ") + else: + count.inc + when compiles($value): + when value isnot string and value isnot seq and compiles(value.isNil): + if value.isNil: result.add "nil" + else: result.addQuoted(value) + else: + result.addQuoted(value) + firstElement = false + else: + result.add("...") + firstElement = false + when not isNamed: + if count == 1: + result.add(',') # $(1,) should print as the semantically legal (1,) + result.add(')') + +proc repr*[T: (ref object)](x: T): string = + ## Generic `repr` operator for tuples that is lifted from the components + ## of `x`. + if x == nil: return "nil" + result = $typeof(x) & "(" + var firstElement = true + for name, value in fieldPairs(x[]): + if not firstElement: result.add(", ") + result.add(name) + result.add(": ") + when compiles($value): + when value isnot string and value isnot seq and compiles(value.isNil): + if value.isNil: result.add "nil" + else: result.addQuoted(value) + else: + result.addQuoted(value) + firstElement = false + else: + result.add("...") + firstElement = false + result.add(')') + +proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string = + result = prefix + var firstElement = true + for value in items(x): + if firstElement: + firstElement = false + else: + result.add(separator) + + when value isnot string and value isnot seq and compiles(value.isNil): + # this branch should not be necessary + if value.isNil: + result.add "nil" + else: + result.addQuoted(value) + else: + result.addQuoted(value) + result.add(suffix) + +proc repr*[T](x: set[T]): string = + ## Generic `repr` operator for sets that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: Nim + ## ${23, 45} == "{23, 45}" + collectionToRepr(x, "{", ", ", "}") + +proc repr*[T](x: seq[T]): string = + ## Generic `repr` operator for seqs that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: Nim + ## $(@[23, 45]) == "@[23, 45]" + collectionToRepr(x, "@[", ", ", "]") + +proc repr*[T, U](x: HSlice[T, U]): string = + ## Generic `repr` operator for slices that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: Nim + ## $(1 .. 5) == "1 .. 5" + result = $x.a + result.add(" .. ") + result.add($x.b) + +proc repr*[T, IDX](x: array[IDX, T]): string = + ## Generic `repr` operator for arrays that is lifted from the components. + collectionToRepr(x, "[", ", ", "]") + +proc repr*[T](x: openArray[T]): string = + ## Generic `repr` operator for openarrays that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: Nim + ## $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]" + collectionToRepr(x, "[", ", ", "]") diff --git a/tests/destructor/tfinalizer.nim b/tests/destructor/tfinalizer.nim new file mode 100644 index 000000000000..f8c1fadfb5ee --- /dev/null +++ b/tests/destructor/tfinalizer.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c --gc:arc $file" + output: '''Foo(field: "Dick Laurent", k: ka, x: 0.0) +Dick Laurent is dead''' +""" + +type + Kind = enum + ka, kb + Foo = ref object + field: string + case k: Kind + of ka: x: float + of kb: discard + +#var x = Foo(field: "lovely") +proc finalizer(x: Foo) = + echo x.field, " is dead" + +var x: Foo +new(x, finalizer) +x.field = "Dick Laurent" +# reference to a great movie. If you haven't seen it, highly recommended. + +echo repr x