typetraits: add toSigned, toUnsigned (#18445)

* typetraits: add toSigned, toUnsigned

* improve and add tests

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: flywind <xzsflywind@gmail.com>
This commit is contained in:
Timothee Cour
2022-04-07 14:38:01 -07:00
committed by GitHub
parent 1807de38e5
commit e78ef57c93
7 changed files with 85 additions and 33 deletions

View File

@@ -27,7 +27,7 @@
import macros
import std/private/since
from std/private/bitops_utils import forwardImpl, toUnsigned
from std/private/bitops_utils import forwardImpl, castToUnsigned
func bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI".}
## Computes the `bitwise complement` of the integer `x`.
@@ -72,7 +72,7 @@ func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1,
let
upmost = sizeof(T) * 8 - 1
uv = when v is SomeUnsignedInt: v else: v.toUnsigned
uv = v.castToUnsigned
(uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T
proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
@@ -84,7 +84,7 @@ proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1,
let
upmost = sizeof(T) * 8 - 1
uv = when v is SomeUnsignedInt: v else: v.toUnsigned
uv = v.castToUnsigned
v = (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T
func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} =
@@ -95,10 +95,7 @@ func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} =
let
upmost = sizeof(T) * 8 - 1
bitmask = when T is SomeUnsignedInt:
bitnot(0.T)
else:
bitnot(0.T).toUnsigned
bitmask = bitnot(0.T).castToUnsigned
(bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T
proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
@@ -505,8 +502,7 @@ func parityBits*(x: SomeInteger): int {.inline.} =
# Can be used a base if creating ASM version.
# https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd
when x is SomeSignedInt:
let x = x.toUnsigned
let x = x.castToUnsigned
when nimvm:
result = forwardImpl(parityImpl, x)
else:
@@ -529,8 +525,7 @@ func firstSetBit*(x: SomeInteger): int {.inline.} =
doAssert firstSetBit(0b0000_1111'u8) == 1
# GCC builtin 'builtin_ffs' already handle zero input.
when x is SomeSignedInt:
let x = x.toUnsigned
let x = x.castToUnsigned
when nimvm:
when noUndefined:
if x == 0:
@@ -572,8 +567,7 @@ func fastLog2*(x: SomeInteger): int {.inline.} =
doAssert fastLog2(0b0000_1000'u8) == 3
doAssert fastLog2(0b0000_1111'u8) == 3
when x is SomeSignedInt:
let x = x.toUnsigned
let x = x.castToUnsigned
when noUndefined:
if x == 0:
return -1
@@ -615,8 +609,7 @@ func countLeadingZeroBits*(x: SomeInteger): int {.inline.} =
doAssert countLeadingZeroBits(0b0000_1000'u8) == 4
doAssert countLeadingZeroBits(0b0000_1111'u8) == 4
when x is SomeSignedInt:
let x = x.toUnsigned
let x = x.castToUnsigned
when noUndefined:
if x == 0:
return 0
@@ -644,8 +637,7 @@ func countTrailingZeroBits*(x: SomeInteger): int {.inline.} =
doAssert countTrailingZeroBits(0b0000_1000'u8) == 3
doAssert countTrailingZeroBits(0b0000_1111'u8) == 0
when x is SomeSignedInt:
let x = x.toUnsigned
let x = x.castToUnsigned
when noUndefined:
if x == 0:
return 0

View File

@@ -35,7 +35,7 @@ runnableExamples:
assert C[float] is HoleyEnum
proc name*(t: typedesc): string {.magic: "TypeTrait".} =
## Returns the name of the given type.
## Returns the name of `t`.
##
## Alias for `system.\`$\`(t) <dollars.html#$,typedesc>`_ since Nim v0.20.
runnableExamples:
@@ -43,7 +43,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".} =
doAssert name(seq[string]) == "seq[string]"
proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
## Returns the arity of the given type. This is the number of "type"
## Returns the arity of `t`. This is the number of "type"
## components or the number of generic parameters a given type `t` has.
runnableExamples:
doAssert arity(int) == 0
@@ -92,8 +92,7 @@ proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} =
doAssert stripGenericParams(int) is int
proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
## This trait returns true if the type `t` is safe to use for
## `copyMem`:idx:.
## Returns true if `t` is safe to use for `copyMem`:idx:.
##
## Other languages name a type like these `blob`:idx:.
@@ -289,9 +288,38 @@ since (1, 1):
proc hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} =
## Return true if the func/proc/etc `fn` has `closure`.
## Returns true if the func/proc/etc `fn` has `closure`.
## `fn` has to be a resolved symbol of kind `nnkSym`. This
## implies that the macro that calls this proc should accept `typed`
## arguments and not `untyped` arguments.
expectKind fn, nnkSym
result = hasClosureImpl(fn)
template toUnsigned*(T: typedesc[SomeInteger and not range]): untyped =
## Returns an unsigned type with same bit size as `T`.
runnableExamples:
assert int8.toUnsigned is uint8
assert uint.toUnsigned is uint
assert int.toUnsigned is uint
# range types are currently unsupported:
assert not compiles(toUnsigned(range[0..7]))
when T is int8: uint8
elif T is int16: uint16
elif T is int32: uint32
elif T is int64: uint64
elif T is int: uint
else: T
template toSigned*(T: typedesc[SomeInteger and not range]): untyped =
## Returns a signed type with same bit size as `T`.
runnableExamples:
assert int8.toSigned is int8
assert uint16.toSigned is int16
# range types are currently unsupported:
assert not compiles(toSigned(range[0..7]))
when T is uint8: int8
elif T is uint16: int16
elif T is uint32: int32
elif T is uint64: int64
elif T is uint: int
else: T

View File

@@ -10,8 +10,13 @@ template forwardImpl*(impl, arg) {.dirty.} =
else:
impl(x.uint64)
template toUnsigned*(x: int8): uint8 = cast[uint8](x)
template toUnsigned*(x: int16): uint16 = cast[uint16](x)
template toUnsigned*(x: int32): uint32 = cast[uint32](x)
template toUnsigned*(x: int64): uint64 = cast[uint64](x)
template toUnsigned*(x: int): uint = cast[uint](x)
# this could also be implemented via:
# import std/typetraits
# template castToUnsigned*(x: SomeInteger): auto = cast[toUnsigned(typeof(x))](x)
template castToUnsigned*(x: int8): uint8 = cast[uint8](x)
template castToUnsigned*(x: int16): uint16 = cast[uint16](x)
template castToUnsigned*(x: int32): uint32 = cast[uint32](x)
template castToUnsigned*(x: int64): uint64 = cast[uint64](x)
template castToUnsigned*(x: int): uint = cast[uint](x)
template castToUnsigned*[T: SomeUnsignedInt](x: T): T = x

View File

@@ -9,8 +9,7 @@
## Contains the used algorithms for counting bits.
from std/private/bitops_utils import forwardImpl, toUnsigned
from std/private/bitops_utils import forwardImpl, castToUnsigned
const useBuiltins* = not defined(noIntrinsicsBitOpts)
const noUndefined* = defined(noUndefinedBitOpts)
@@ -65,8 +64,7 @@ func countSetBitsImpl*(x: SomeInteger): int {.inline.} =
## Counts the set bits in an integer (also called `Hamming weight`:idx:).
# TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
# like GCC and MSVC
when x is SomeSignedInt:
let x = x.toUnsigned
let x = x.castToUnsigned
when nimvm:
result = forwardImpl(countBitsImpl, x)
else:

View File

@@ -1,6 +1,21 @@
import typetraits
import macros
block: # toUnsigned, toSigned
var a1: toSigned(int16)
doAssert a1 is int16
var a2: toSigned(uint16)
doAssert $a2.typeof == "int16"
doAssert toSigned(uint32) is int32
doAssert uint64.toSigned is int64
doAssert int64.toSigned is int64
doAssert int64.toUnsigned is uint64
doAssert int.toUnsigned is uint
doAssert $uint.toUnsigned == "uint"
# disallowed for now
doAssert not compiles(toUnsigned(range[0..7]))
doAssert not compiles(toSigned(range[0..7]))
block: # isNamedTuple
type Foo1 = (a:1,).type
type Foo2 = (Field0:1,).type

View File

@@ -0,0 +1,14 @@
import std/private/bitops_utils
template chk(a, b) =
let a2 = castToUnsigned(a)
doAssert a2 == b
doAssert type(a2) is type(b)
doAssert type(b) is type(a2)
chk 1'i8, 1'u8
chk -1'i8, 255'u8
chk 1'u8, 1'u8
chk 1'u, 1'u
chk -1, cast[uint](-1)
chk -1'i64, cast[uint64](-1)

View File

@@ -2,10 +2,10 @@ discard """
targets: "c cpp js"
"""
# xxx merge with tests/metatype/ttypetraits.nim
import std/typetraits
macro testClosure(fn: typed, flag: static bool) =
if flag:
doAssert hasClosure(fn)