diff --git a/doc/manual.rst b/doc/manual.rst index abdc4ce695..de0a131434 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1673,7 +1673,8 @@ A ``distinct`` type is new type derived from a `base type`:idx: that is incompatible with its base type. In particular, it is an essential property of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its -base type and vice versa are allowed. +base type and vice versa are allowed. See also ``distinctBase`` to get the +reverse operation. Modelling currencies diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 258b40191a..8ded552d9d 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -198,3 +198,41 @@ macro dump*(x: typed): untyped = let r = quote do: debugEcho `s`, " = ", `x` return r + +# TODO: consider exporting this in macros.nim +proc freshIdentNodes(ast: NimNode): NimNode = + # Replace NimIdent and NimSym by a fresh ident node + # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 + proc inspect(node: NimNode): NimNode = + case node.kind: + of nnkIdent, nnkSym: + result = ident($node) + of nnkEmpty, nnkLiterals: + result = node + else: + result = node.kind.newTree() + for child in node: + result.add inspect(child) + result = inspect(ast) + +macro distinctBase*(T: typedesc): untyped = + ## reverses ``type T = distinct A``; works recursively. + runnableExamples: + type T = distinct int + doAssert distinctBase(T) is int + doAssert: not compiles(distinctBase(int)) + type T2 = distinct T + doAssert distinctBase(T2) is int + + let typeNode = getTypeImpl(T) + expectKind(typeNode, nnkBracketExpr) + if typeNode[0].typeKind != ntyTypeDesc: + error "expected typeDesc, got " & $typeNode[0] + var typeSym = typeNode[1] + typeSym = getTypeImpl(typeSym) + if typeSym.typeKind != ntyDistinct: + error "type is not distinct" + typeSym = typeSym[0] + while typeSym.typeKind == ntyDistinct: + typeSym = getTypeImpl(typeSym)[0] + typeSym.freshIdentNodes diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim new file mode 100644 index 0000000000..a870bf6fed --- /dev/null +++ b/tests/stdlib/tsugar.nim @@ -0,0 +1,29 @@ +discard """ + file: "tsugar.nim" + output: "" +""" +import sugar +import macros + +block distinctBase: + block: + type + Foo[T] = distinct seq[T] + var a: Foo[int] + doAssert a.type.distinctBase is seq[int] + + block: + # simplified from https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 + macro uintImpl(bits: static[int]): untyped = + if bits >= 128: + let inner = getAST(uintImpl(bits div 2)) + result = newTree(nnkBracketExpr, ident("UintImpl"), inner) + else: + result = ident("uint64") + + type + BaseUint = UintImpl or SomeUnsignedInt + UintImpl[Baseuint] = object + Uint[bits: static[int]] = distinct uintImpl(bits) + + doAssert Uint[128].distinctBase is UintImpl[uint64]