diff options
author | Timothee Cour <timothee.cour2@gmail.com> | 2022-04-07 14:38:01 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-07 17:38:01 -0400 |
commit | e78ef57c93d66da483e0482ce0907dfe16dc8d27 (patch) | |
tree | 32ba3900b479bc2e046aca563df99c1be77a5d9d | |
parent | 1807de38e52f45c2fb88dac9b99b47729b12ebae (diff) | |
download | Nim-e78ef57c93d66da483e0482ce0907dfe16dc8d27.tar.gz |
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>
-rw-r--r-- | lib/pure/bitops.nim | 26 | ||||
-rw-r--r-- | lib/pure/typetraits.nim | 38 | ||||
-rw-r--r-- | lib/std/private/bitops_utils.nim | 15 | ||||
-rw-r--r-- | lib/system/countbits_impl.nim | 6 | ||||
-rw-r--r-- | tests/metatype/ttypetraits.nim | 15 | ||||
-rw-r--r-- | tests/stdlib/tbitops_utils.nim | 14 | ||||
-rw-r--r-- | tests/stdlib/ttypetraits.nim | 4 |
7 files changed, 85 insertions, 33 deletions
diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 1b3838621..a518c25d2 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -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 diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 3fc1c7c5c..c20f9e645 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -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 diff --git a/lib/std/private/bitops_utils.nim b/lib/std/private/bitops_utils.nim index d54977b4e..0b9484416 100644 --- a/lib/std/private/bitops_utils.nim +++ b/lib/std/private/bitops_utils.nim @@ -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 diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim index 020423e01..34969cb32 100644 --- a/lib/system/countbits_impl.nim +++ b/lib/system/countbits_impl.nim @@ -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: diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index 3ff5c5ea6..bfaa23057 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -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 diff --git a/tests/stdlib/tbitops_utils.nim b/tests/stdlib/tbitops_utils.nim new file mode 100644 index 000000000..b571baeae --- /dev/null +++ b/tests/stdlib/tbitops_utils.nim @@ -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) diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim index de8259ab0..799bcf6e2 100644 --- a/tests/stdlib/ttypetraits.nim +++ b/tests/stdlib/ttypetraits.nim @@ -2,9 +2,9 @@ discard """ targets: "c cpp js" """ -import std/typetraits - +# xxx merge with tests/metatype/ttypetraits.nim +import std/typetraits macro testClosure(fn: typed, flag: static bool) = if flag: |