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:
parent
1807de38e5
commit
e78ef57c93
7 changed files with 85 additions and 33 deletions
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
import std/private/since
|
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".}
|
func bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI".}
|
||||||
## Computes the `bitwise complement` of the integer `x`.
|
## 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
|
let
|
||||||
upmost = sizeof(T) * 8 - 1
|
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
|
(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).} =
|
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
|
let
|
||||||
upmost = sizeof(T) * 8 - 1
|
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
|
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).} =
|
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
|
let
|
||||||
upmost = sizeof(T) * 8 - 1
|
upmost = sizeof(T) * 8 - 1
|
||||||
bitmask = when T is SomeUnsignedInt:
|
bitmask = bitnot(0.T).castToUnsigned
|
||||||
bitnot(0.T)
|
|
||||||
else:
|
|
||||||
bitnot(0.T).toUnsigned
|
|
||||||
(bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T
|
(bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T
|
||||||
|
|
||||||
proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
|
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.
|
# 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
|
# https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd
|
||||||
when x is SomeSignedInt:
|
let x = x.castToUnsigned
|
||||||
let x = x.toUnsigned
|
|
||||||
when nimvm:
|
when nimvm:
|
||||||
result = forwardImpl(parityImpl, x)
|
result = forwardImpl(parityImpl, x)
|
||||||
else:
|
else:
|
||||||
|
|
@ -529,8 +525,7 @@ func firstSetBit*(x: SomeInteger): int {.inline.} =
|
||||||
doAssert firstSetBit(0b0000_1111'u8) == 1
|
doAssert firstSetBit(0b0000_1111'u8) == 1
|
||||||
|
|
||||||
# GCC builtin 'builtin_ffs' already handle zero input.
|
# GCC builtin 'builtin_ffs' already handle zero input.
|
||||||
when x is SomeSignedInt:
|
let x = x.castToUnsigned
|
||||||
let x = x.toUnsigned
|
|
||||||
when nimvm:
|
when nimvm:
|
||||||
when noUndefined:
|
when noUndefined:
|
||||||
if x == 0:
|
if x == 0:
|
||||||
|
|
@ -572,8 +567,7 @@ func fastLog2*(x: SomeInteger): int {.inline.} =
|
||||||
doAssert fastLog2(0b0000_1000'u8) == 3
|
doAssert fastLog2(0b0000_1000'u8) == 3
|
||||||
doAssert fastLog2(0b0000_1111'u8) == 3
|
doAssert fastLog2(0b0000_1111'u8) == 3
|
||||||
|
|
||||||
when x is SomeSignedInt:
|
let x = x.castToUnsigned
|
||||||
let x = x.toUnsigned
|
|
||||||
when noUndefined:
|
when noUndefined:
|
||||||
if x == 0:
|
if x == 0:
|
||||||
return -1
|
return -1
|
||||||
|
|
@ -615,8 +609,7 @@ func countLeadingZeroBits*(x: SomeInteger): int {.inline.} =
|
||||||
doAssert countLeadingZeroBits(0b0000_1000'u8) == 4
|
doAssert countLeadingZeroBits(0b0000_1000'u8) == 4
|
||||||
doAssert countLeadingZeroBits(0b0000_1111'u8) == 4
|
doAssert countLeadingZeroBits(0b0000_1111'u8) == 4
|
||||||
|
|
||||||
when x is SomeSignedInt:
|
let x = x.castToUnsigned
|
||||||
let x = x.toUnsigned
|
|
||||||
when noUndefined:
|
when noUndefined:
|
||||||
if x == 0:
|
if x == 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -644,8 +637,7 @@ func countTrailingZeroBits*(x: SomeInteger): int {.inline.} =
|
||||||
doAssert countTrailingZeroBits(0b0000_1000'u8) == 3
|
doAssert countTrailingZeroBits(0b0000_1000'u8) == 3
|
||||||
doAssert countTrailingZeroBits(0b0000_1111'u8) == 0
|
doAssert countTrailingZeroBits(0b0000_1111'u8) == 0
|
||||||
|
|
||||||
when x is SomeSignedInt:
|
let x = x.castToUnsigned
|
||||||
let x = x.toUnsigned
|
|
||||||
when noUndefined:
|
when noUndefined:
|
||||||
if x == 0:
|
if x == 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ runnableExamples:
|
||||||
assert C[float] is HoleyEnum
|
assert C[float] is HoleyEnum
|
||||||
|
|
||||||
proc name*(t: typedesc): string {.magic: "TypeTrait".} =
|
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.
|
## Alias for `system.\`$\`(t) <dollars.html#$,typedesc>`_ since Nim v0.20.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
|
|
@ -43,7 +43,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".} =
|
||||||
doAssert name(seq[string]) == "seq[string]"
|
doAssert name(seq[string]) == "seq[string]"
|
||||||
|
|
||||||
proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
|
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.
|
## components or the number of generic parameters a given type `t` has.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
doAssert arity(int) == 0
|
doAssert arity(int) == 0
|
||||||
|
|
@ -92,8 +92,7 @@ proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} =
|
||||||
doAssert stripGenericParams(int) is int
|
doAssert stripGenericParams(int) is int
|
||||||
|
|
||||||
proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
|
proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
|
||||||
## This trait returns true if the type `t` is safe to use for
|
## Returns true if `t` is safe to use for `copyMem`:idx:.
|
||||||
## `copyMem`:idx:.
|
|
||||||
##
|
##
|
||||||
## Other languages name a type like these `blob`: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 hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
|
||||||
|
|
||||||
proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} =
|
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
|
## `fn` has to be a resolved symbol of kind `nnkSym`. This
|
||||||
## implies that the macro that calls this proc should accept `typed`
|
## implies that the macro that calls this proc should accept `typed`
|
||||||
## arguments and not `untyped` arguments.
|
## arguments and not `untyped` arguments.
|
||||||
expectKind fn, nnkSym
|
expectKind fn, nnkSym
|
||||||
result = hasClosureImpl(fn)
|
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
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,13 @@ template forwardImpl*(impl, arg) {.dirty.} =
|
||||||
else:
|
else:
|
||||||
impl(x.uint64)
|
impl(x.uint64)
|
||||||
|
|
||||||
template toUnsigned*(x: int8): uint8 = cast[uint8](x)
|
# this could also be implemented via:
|
||||||
template toUnsigned*(x: int16): uint16 = cast[uint16](x)
|
# import std/typetraits
|
||||||
template toUnsigned*(x: int32): uint32 = cast[uint32](x)
|
# template castToUnsigned*(x: SomeInteger): auto = cast[toUnsigned(typeof(x))](x)
|
||||||
template toUnsigned*(x: int64): uint64 = cast[uint64](x)
|
|
||||||
template toUnsigned*(x: int): uint = cast[uint](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
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
|
|
||||||
## Contains the used algorithms for counting bits.
|
## 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 useBuiltins* = not defined(noIntrinsicsBitOpts)
|
||||||
const noUndefined* = defined(noUndefinedBitOpts)
|
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:).
|
## Counts the set bits in an integer (also called `Hamming weight`:idx:).
|
||||||
# TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
|
# TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
|
||||||
# like GCC and MSVC
|
# like GCC and MSVC
|
||||||
when x is SomeSignedInt:
|
let x = x.castToUnsigned
|
||||||
let x = x.toUnsigned
|
|
||||||
when nimvm:
|
when nimvm:
|
||||||
result = forwardImpl(countBitsImpl, x)
|
result = forwardImpl(countBitsImpl, x)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,21 @@
|
||||||
import typetraits
|
import typetraits
|
||||||
import macros
|
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
|
block: # isNamedTuple
|
||||||
type Foo1 = (a:1,).type
|
type Foo1 = (a:1,).type
|
||||||
type Foo2 = (Field0:1,).type
|
type Foo2 = (Field0:1,).type
|
||||||
|
|
|
||||||
14
tests/stdlib/tbitops_utils.nim
Normal file
14
tests/stdlib/tbitops_utils.nim
Normal 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)
|
||||||
|
|
@ -2,10 +2,10 @@ discard """
|
||||||
targets: "c cpp js"
|
targets: "c cpp js"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# xxx merge with tests/metatype/ttypetraits.nim
|
||||||
|
|
||||||
import std/typetraits
|
import std/typetraits
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
macro testClosure(fn: typed, flag: static bool) =
|
macro testClosure(fn: typed, flag: static bool) =
|
||||||
if flag:
|
if flag:
|
||||||
doAssert hasClosure(fn)
|
doAssert hasClosure(fn)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue