mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-24 00:05:25 +00:00
Merge pull request #12107 from nim-lang/uint-range-checks
Uint range checks
This commit is contained in:
@@ -71,6 +71,9 @@ iterator mypairs[T](x: openarray[T]): tuple[idx: int, val: lent T]
|
||||
|
||||
## Language changes
|
||||
|
||||
- `uint64` is now finally a regular ordinal type. This means `high(uint64)` compiles
|
||||
and yields the correct value.
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ proc sameValue*(a, b: PNode): bool =
|
||||
result = false
|
||||
case a.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal
|
||||
if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) == getInt(b)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
@@ -130,8 +130,8 @@ proc leValue*(a, b: PNode): bool =
|
||||
# a <= b?
|
||||
result = false
|
||||
case a.kind
|
||||
of nkCharLit..nkUInt32Lit:
|
||||
if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) <= getInt(b)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
|
||||
@@ -16,13 +16,6 @@ template sdata(arg: Int128, idx: int): int32 =
|
||||
|
||||
# encoding least significant int first (like LittleEndian)
|
||||
|
||||
type
|
||||
InvalidArgument = object of Exception
|
||||
|
||||
template require(cond: bool) =
|
||||
if unlikely(not cond):
|
||||
raise newException(InvalidArgument, "")
|
||||
|
||||
const
|
||||
Zero* = Int128(udata: [0'u32,0,0,0])
|
||||
One* = Int128(udata: [1'u32,0,0,0])
|
||||
@@ -378,7 +371,6 @@ proc `*`*(lhs,rhs: Int128): Int128 =
|
||||
result = result + toInt128(a00 * b32) shl 32
|
||||
|
||||
if isNegative != isNegative(result):
|
||||
echo result
|
||||
assert(false, "overflow")
|
||||
|
||||
proc `*=`*(a: var Int128, b: Int128) =
|
||||
|
||||
@@ -565,7 +565,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||
case result.tokType
|
||||
of floatTypes:
|
||||
result.fNumber = parseFloat(result.literal)
|
||||
of tkUInt64Lit:
|
||||
of tkUInt64Lit, tkUIntLit:
|
||||
var iNumber: uint64
|
||||
var len: int
|
||||
try:
|
||||
|
||||
@@ -149,7 +149,7 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
(srcBaseTyp.kind in IntegralTypes):
|
||||
if targetTyp.isOrdinalType:
|
||||
if src.kind in nkCharLit..nkUInt64Lit and
|
||||
src.intVal notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp):
|
||||
src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp):
|
||||
result = convNotInRange
|
||||
elif src.kind in nkFloatLit..nkFloat64Lit and
|
||||
(classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
|
||||
@@ -341,7 +341,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
n.typ = getSysType(c.graph, n.info, tyInt)
|
||||
of tyArray:
|
||||
n.typ = typ.sons[0] # indextype
|
||||
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32, tyFloat..tyFloat64:
|
||||
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64:
|
||||
n.typ = n.sons[1].typ.skipTypes({tyTypeDesc})
|
||||
of tyGenericParam:
|
||||
# prepare this for resolving in semtypinst:
|
||||
|
||||
@@ -205,8 +205,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
of mSubI: result = foldSub(getInt(a), getInt(b), n, g)
|
||||
of mMulI: result = foldMul(getInt(a), getInt(b), n, g)
|
||||
of mMinI:
|
||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt64(b), n, g)
|
||||
else: result = newIntNodeT(getInt64(a), n, g)
|
||||
let argA = getInt(a)
|
||||
let argB = getInt(b)
|
||||
result = newIntNodeT(if argA < argB: argA else: argB, n, g)
|
||||
of mMaxI:
|
||||
let argA = getInt(a)
|
||||
let argB = getInt(b)
|
||||
@@ -388,13 +389,13 @@ proc leValueConv*(a, b: PNode): bool =
|
||||
case a.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
case b.kind
|
||||
of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
|
||||
of nkCharLit..nkUInt64Lit: result = a.getInt <= b.getInt
|
||||
of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int
|
||||
else: result = false #internalError(a.info, "leValueConv")
|
||||
of nkFloatLit..nkFloat128Lit:
|
||||
case b.kind
|
||||
of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal
|
||||
of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
|
||||
of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat64(b.getInt)
|
||||
else: result = false # internalError(a.info, "leValueConv")
|
||||
else: result = false # internalError(a.info, "leValueConv")
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
if not hasUnknownTypes:
|
||||
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
|
||||
localError(c.config, n.info, "type mismatch")
|
||||
elif not rangeT[0].isOrdinalType and rangeT[0].kind notin tyFloat..tyFloat128 or
|
||||
elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin tyFloat..tyFloat128 or
|
||||
rangeT[0].kind == tyBool:
|
||||
localError(c.config, n.info, "ordinal or float type expected")
|
||||
elif enumHasHoles(rangeT[0]):
|
||||
|
||||
@@ -717,7 +717,7 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
|
||||
result.add(elseBranch)
|
||||
elif result.PNode.lastSon.kind != nkElse and not (
|
||||
skipTypes(n.sons[0].typ, abstractVarRange).kind in
|
||||
{tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}):
|
||||
{tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64}):
|
||||
# fix a stupid code gen bug by normalizing:
|
||||
var elseBranch = newTransNode(nkElse, n.info, 1)
|
||||
elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
|
||||
|
||||
@@ -162,14 +162,13 @@ proc enumHasHoles*(t: PType): bool =
|
||||
var b = t.skipTypes({tyRange, tyGenericInst, tyAlias, tySink})
|
||||
result = b.kind == tyEnum and tfEnumHasHoles in b.flags
|
||||
|
||||
proc isOrdinalType*(t: PType, allowEnumWithHoles = false): bool =
|
||||
proc isOrdinalType*(t: PType, allowEnumWithHoles: bool = false): bool =
|
||||
assert(t != nil)
|
||||
const
|
||||
# caution: uint, uint64 are no ordinal types!
|
||||
baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum}
|
||||
baseKinds = {tyChar,tyInt..tyInt64,tyUInt..tyUInt64,tyBool,tyEnum}
|
||||
parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct}
|
||||
(t.kind in baseKinds and not (t.enumHasHoles and not allowEnumWithHoles)) or
|
||||
(t.kind in parentKinds and isOrdinalType(t.lastSon))
|
||||
result = (t.kind in baseKinds and (not t.enumHasHoles or allowEnumWithHoles)) or
|
||||
(t.kind in parentKinds and isOrdinalType(t.lastSon, allowEnumWithHoles))
|
||||
|
||||
proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
|
||||
closure: RootRef): bool
|
||||
@@ -1319,7 +1318,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
result = typeAllowedAux(marker, lastSon(t), kind, flags)
|
||||
of tyRange:
|
||||
if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin
|
||||
{tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t
|
||||
{tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64}: result = t
|
||||
of tyOpenArray, tyVarargs, tySink:
|
||||
# you cannot nest openArrays/sinks/etc.
|
||||
if kind != skParam or taIsOpenArray in flags:
|
||||
|
||||
@@ -682,6 +682,7 @@ proc genUnaryABI(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; imm: BiggestI
|
||||
c.gABI(n, opc, dest, tmp, imm)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
|
||||
proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
let
|
||||
tmp = c.genx(n.sons[1])
|
||||
@@ -999,22 +1000,15 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
|
||||
of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
|
||||
of mShrI:
|
||||
# the idea here is to narrow type if needed before executing right shift
|
||||
# inlined modified: genNarrowU(c, n, dest)
|
||||
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
# uint is uint64 in the VM, we we only need to mask the result for
|
||||
# other unsigned types:
|
||||
# modified: genBinaryABC(c, n, dest, opcShrInt)
|
||||
# narrowU is applied to the left operandthe idea here is to narrow the left operand
|
||||
let tmp = c.genx(n.sons[1])
|
||||
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
|
||||
c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8))
|
||||
|
||||
# inlined modified: genBinaryABC(c, n, dest, opcShrInt)
|
||||
c.genNarrowU(n, tmp)
|
||||
let tmp2 = c.genx(n.sons[2])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcShrInt, dest, tmp, tmp2)
|
||||
c.freeTemp(tmp)
|
||||
c.freeTemp(tmp2)
|
||||
|
||||
of mShlI:
|
||||
genBinaryABC(c, n, dest, opcShlInt)
|
||||
# genNarrowU modified
|
||||
|
||||
@@ -97,7 +97,7 @@ type
|
||||
SomeInteger* = SomeSignedInt|SomeUnsignedInt
|
||||
## Type class matching all integer types.
|
||||
|
||||
SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32
|
||||
SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint|uint8|uint16|uint32|uint64
|
||||
## Type class matching all ordinal types; however this includes enums with
|
||||
## holes.
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ block tcast:
|
||||
crossCheck(uint16, uint16.high + 5'u16)
|
||||
crossCheck(uint32, uint32.high + 5'u32)
|
||||
crossCheck(uint64, 0xFFFFFFFFFFFFFFFF'u64 + 5'u64)
|
||||
crossCheck(uint64, uint64.high + 5'u64)
|
||||
|
||||
doAssert $sub1(0'u8) == "255"
|
||||
doAssert $sub1(0'u16) == "65535"
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
discard """
|
||||
action: run
|
||||
output: '''
|
||||
18446744073709551615
|
||||
9223372036854775807
|
||||
4294967295
|
||||
0
|
||||
0
|
||||
'''
|
||||
"""
|
||||
|
||||
var x: range[-1'f32..1'f32]
|
||||
@@ -16,3 +23,10 @@ doAssert y.type.high == 1'f64
|
||||
# bug #11972
|
||||
var num: uint8
|
||||
doAssert num.high.float == 255.0
|
||||
|
||||
echo high(uint64)
|
||||
echo high(int64)
|
||||
echo high(uint32)
|
||||
|
||||
echo low(uint64)
|
||||
echo low(uint32)
|
||||
|
||||
52
tests/range/tcompiletime_range_checks.nim
Normal file
52
tests/range/tcompiletime_range_checks.nim
Normal file
@@ -0,0 +1,52 @@
|
||||
discard """
|
||||
cmd: "nim check --hint[Processing]:off --hint[Conf]:off $file"
|
||||
errormsg: "18446744073709551615 can't be converted to int8"
|
||||
nimout: '''tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32
|
||||
tcompiletime_range_checks.nim(37, 23) Error: -1 can't be converted to uint64
|
||||
tcompiletime_range_checks.nim(38, 34) Error: 255 can't be converted to FullNegativeRange
|
||||
tcompiletime_range_checks.nim(39, 34) Error: 18446744073709551615 can't be converted to HalfNegativeRange
|
||||
tcompiletime_range_checks.nim(40, 34) Error: 300 can't be converted to FullPositiveRange
|
||||
tcompiletime_range_checks.nim(41, 30) Error: 101 can't be converted to UnsignedRange
|
||||
tcompiletime_range_checks.nim(42, 32) Error: -9223372036854775808 can't be converted to SemiOutOfBounds
|
||||
tcompiletime_range_checks.nim(44, 22) Error: nan can't be converted to int32
|
||||
tcompiletime_range_checks.nim(46, 23) Error: 1e+100 can't be converted to uint64
|
||||
tcompiletime_range_checks.nim(49, 22) Error: 18446744073709551615 can't be converted to int64
|
||||
tcompiletime_range_checks.nim(50, 22) Error: 18446744073709551615 can't be converted to int32
|
||||
tcompiletime_range_checks.nim(51, 22) Error: 18446744073709551615 can't be converted to int16
|
||||
tcompiletime_range_checks.nim(52, 21) Error: 18446744073709551615 can't be converted to int8
|
||||
'''
|
||||
"""
|
||||
|
||||
type
|
||||
UnsignedRange* = range[0'u64 .. 100'u64]
|
||||
SemiOutOfBounds* = range[0x7ffffffffffffe00'u64 .. 0x8000000000000100'u64]
|
||||
FullOutOfBounds* = range[0x8000000000000000'u64 .. 0x8000000000000200'u64]
|
||||
|
||||
FullNegativeRange* = range[-200 .. -100]
|
||||
HalfNegativeRange* = range[-50 .. 50]
|
||||
FullPositiveRange* = range[100 .. 200]
|
||||
|
||||
let acceptA* = int32(0x7fffffff'i64)
|
||||
let acceptB* = (uint64(0'i64))
|
||||
let acceptD* = (HalfNegativeRange(25'u64))
|
||||
let acceptE* = (UnsignedRange(50'u64))
|
||||
let acceptF* = (SemiOutOfBounds(0x7ffffffffffffe00'i64))
|
||||
let acceptH* = (SemiOutOfBounds(0x8000000000000000'u64))
|
||||
|
||||
let rejectA* = int32(0x80000000'i64)
|
||||
let rejectB* = (uint64(-1'i64))
|
||||
let rejectC* = (FullNegativeRange(0xff'u32))
|
||||
let rejectD* = (HalfNegativeRange(0xffffffffffffffff'u64)) # internal `intVal` is `-1` which would be in range.
|
||||
let rejectE* = (FullPositiveRange(300'u64))
|
||||
let rejectF* = (UnsignedRange(101'u64))
|
||||
let rejectG* = (SemiOutOfBounds(0x8000000000000000'i64)) #
|
||||
|
||||
let rejectH* = (int32(NaN))
|
||||
let rejectI* = (int64(1e100))
|
||||
let rejectJ* = (uint64(1e100))
|
||||
|
||||
# removed cross checks from tarithm.nim
|
||||
let rejectK* = (int64(0xFFFFFFFFFFFFFFFF'u64))
|
||||
let rejectL* = (int32(0xFFFFFFFFFFFFFFFF'u64))
|
||||
let rejectM* = (int16(0xFFFFFFFFFFFFFFFF'u64))
|
||||
let rejectN* = (int8(0xFFFFFFFFFFFFFFFF'u64))
|
||||
@@ -211,15 +211,15 @@ proc main() =
|
||||
|
||||
proc testReverseBitsPerType(x, reversed: uint64) =
|
||||
doAssert reverseBits(x) == reversed
|
||||
doAssert reverseBits(uint32(x)) == uint32(reversed shr 32)
|
||||
doAssert reverseBits(uint32(x shr 16)) == uint32(reversed shr 16)
|
||||
doAssert reverseBits(uint16(x)) == uint16(reversed shr 48)
|
||||
doAssert reverseBits(uint8(x)) == uint8(reversed shr 56)
|
||||
doAssert reverseBits(cast[uint32](x)) == cast[uint32](reversed shr 32)
|
||||
doAssert reverseBits(cast[uint32](x shr 16)) == cast[uint32](reversed shr 16)
|
||||
doAssert reverseBits(cast[uint16](x)) == cast[uint16](reversed shr 48)
|
||||
doAssert reverseBits(cast[uint8](x)) == cast[uint8](reversed shr 56)
|
||||
|
||||
testReverseBitsInvo(x)
|
||||
testReverseBitsInvo(uint32(x))
|
||||
testReverseBitsInvo(uint16(x))
|
||||
testReverseBitsInvo(uint8(x))
|
||||
testReverseBitsInvo(cast[uint32](x))
|
||||
testReverseBitsInvo(cast[uint16](x))
|
||||
testReverseBitsInvo(cast[uint8](x))
|
||||
|
||||
proc testReverseBitsRefl(x, reversed: uint64) =
|
||||
testReverseBitsPerType(x, reversed)
|
||||
|
||||
@@ -69,8 +69,8 @@ macro foo2() =
|
||||
echo zz
|
||||
|
||||
var ww = -9
|
||||
var vv = ww.uint
|
||||
var kk = vv.uint32
|
||||
var vv = cast[uint](ww)
|
||||
var kk = cast[uint32](vv)
|
||||
echo kk
|
||||
|
||||
foo()
|
||||
|
||||
@@ -18,9 +18,9 @@ proc get_values(): (seq[int8], seq[int16], seq[int32]) =
|
||||
let i32 = -(1'i64 shl 33) + offset
|
||||
|
||||
# higher bits are masked. these should be exactly equal to offset.
|
||||
result[0].add cast[int8 ](uint8 cast[uint64](i8 ))
|
||||
result[1].add cast[int16](uint16 cast[uint64](i16))
|
||||
result[2].add cast[int32](uint32 cast[uint64](i32))
|
||||
result[0].add cast[int8](cast[uint64](i8))
|
||||
result[1].add cast[int16](cast[uint64](i16))
|
||||
result[2].add cast[int32](cast[uint64](i32))
|
||||
|
||||
|
||||
# these values this computed by VM
|
||||
|
||||
Reference in New Issue
Block a user