Skip to content

distinctBase type trait for distinct types #13031

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
backends, and it is compatible with Python's behavior,
e.g. `formatFloat(3.14159, precision = 0)` is now `3`, not `3.`.
- Global variable `lc` has been removed from sugar.nim.
- `distinctBase` has been moved from sugar.nim to typetraits and now implemented as
compiler type trait instead of macro. `distinctBase` in sugar module is now deprecated.

### Breaking changes in the compiler

Expand Down
16 changes: 16 additions & 0 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,22 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
let complexObj = containsGarbageCollectedRef(t) or
hasDestructor(t)
result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.graph)
of "isNamedTuple":
let cond = operand.kind == tyTuple and operand.n != nil
result = newIntNodeT(toInt128(ord(cond)), traitCall, c.graph)
of "distinctBase":
var arg = operand.skipTypes({tyGenericInst})
if arg.kind == tyDistinct:
while arg.kind == tyDistinct:
arg = arg.base
arg = arg.skipTypes(skippedTypes + {tyGenericInst})
var resType = newType(tyTypeDesc, operand.owner)
rawAddSon(resType, arg)
result = toNode(resType, traitCall.info)
else:
localError(c.config, traitCall.info,
"distinctBase expects a distinct type as argument. The given type was " & typeToString(operand))
result = newType(tyError, context).toNode(traitCall.info)
else:
localError(c.config, traitCall.info, "unknown trait: " & s)
result = newNodeI(nkEmpty, traitCall.info)
Expand Down
23 changes: 3 additions & 20 deletions lib/pure/sugar.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
include system/inclrtl

import macros
import typetraits

proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
#echo treeRepr(p)
Expand Down Expand Up @@ -160,27 +161,9 @@ proc freshIdentNodes(ast: NimNode): NimNode =
result.add inspect(child)
result = inspect(ast)

macro distinctBase*(T: typedesc): untyped =
template distinctBase*(T: typedesc): typedesc {.deprecated: "use distinctBase from typetraits instead".} =
## 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
typetraits.distinctBase(T)

macro capture*(locals: openArray[typed], body: untyped): untyped {.since: (1, 1).} =
## Useful when creating a closure in a loop to capture some local loop variables
Expand Down
17 changes: 5 additions & 12 deletions lib/pure/typetraits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,12 @@ proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
##
## Other languages name a type like these `blob`:idx:.

proc isNamedTuple*(T: typedesc): bool =
proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".}
## Return true for named tuples, false for any other type.
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
# empty tuple should be un-named,
# see https://github.com/nim-lang/Nim/issues/8861#issue-356631191
return false

proc distinctBase*(T: typedesc): typedesc {.magic: "TypeTrait".}
## Returns base type for distinct types, works only for distinct types.
## compile time error otherwise


when isMainModule:
Expand Down
49 changes: 49 additions & 0 deletions tests/metatype/ttypetraits.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import typetraits
import macros

block: # isNamedTuple
type Foo1 = (a:1,).type
Expand All @@ -9,6 +10,7 @@ block: # isNamedTuple
doAssert (a:1,).type.isNamedTuple
doAssert Foo1.isNamedTuple
doAssert Foo2.isNamedTuple
doAssert isNamedTuple(tuple[key: int])
doAssert not Foo3.isNamedTuple
doAssert not Foo4.isNamedTuple
doAssert not (1,).type.isNamedTuple
Expand Down Expand Up @@ -42,3 +44,50 @@ block: # typeToString
doAssert (tuple[a: C2b[MyInt, C4[cstring]], b: cint, c: float]).name3 ==
"tuple[a: C2b{C}[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]"


#----------------------------------------------------

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]

block:
type
AA = distinct seq[int]
BB = distinct string
CC = distinct int
AAA = AA

static:
var a2: AAA
var b2: BB
var c2: CC

doAssert(a2 is distinct)
doAssert(b2 is distinct)
doAssert(c2 is distinct)

doAssert($distinctBase(typeof(a2)) == "seq[int]")
doAssert($distinctBase(typeof(b2)) == "string")
doAssert($distinctBase(typeof(c2)) == "int")


17 changes: 16 additions & 1 deletion tests/stdlib/tstring.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
discard """
output: "OK"
output: '''OK
@[@[], @[], @[], @[], @[]]
'''
"""
const characters = "abcdefghijklmnopqrstuvwxyz"
const numbers = "1234567890"
Expand Down Expand Up @@ -76,3 +78,16 @@ proc test_string_cmp() =

test_string_slice()
test_string_cmp()


#--------------------------
# bug #7816
import sugar
import sequtils

proc tester[T](x: T) =
let test = toSeq(0..4).map(i => newSeq[int]())
echo test

tester(1)

37 changes: 0 additions & 37 deletions tests/stdlib/tsugar.nim

This file was deleted.