mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
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"
|
||||
"""
|
||||
|
||||
# xxx merge with tests/metatype/ttypetraits.nim
|
||||
|
||||
import std/typetraits
|
||||
|
||||
|
||||
|
||||
macro testClosure(fn: typed, flag: static bool) =
|
||||
if flag:
|
||||
doAssert hasClosure(fn)
|
||||
|
||||
Reference in New Issue
Block a user