Don't hide overflow/underflow/divbyzero in semfold (#7732)

* firstOrd/lastOrd bugfix
* Don't hide overflow/underflow/divbyzero in semfold
This commit is contained in:
Oscar Nihlgård 2018-05-02 08:54:09 +02:00 • committed by Andreas Rumpf
commit cc0ca43743
4 changed files with 100 additions and 33 deletions

View file

@ -13,7 +13,7 @@
import import
strutils, options, ast, astalgo, trees, treetab, nimsets, times, strutils, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, renderer, types, nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
commands, magicsys, saturate commands, magicsys
proc getConstExpr*(m: PSym, n: PNode): PNode proc getConstExpr*(m: PSym, n: PNode): PNode
# evaluates the constant expression or returns nil if it is no constant # evaluates the constant expression or returns nil if it is no constant
@ -24,6 +24,63 @@ proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, n: PNode): PNode proc newStrNodeT*(strVal: string, n: PNode): PNode
proc checkInRange(n: PNode, res: BiggestInt): bool =
if res in firstOrd(n.typ)..lastOrd(n.typ):
result = true
proc foldAdd(a, b: BiggestInt, n: PNode): PNode =
let res = a +% b
if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
checkInRange(n, res):
result = newIntNodeT(res, n)
proc foldSub*(a, b: BiggestInt, n: PNode): PNode =
let res = a -% b
if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
checkInRange(n, res):
result = newIntNodeT(res, n)
proc foldAbs*(a: BiggestInt, n: PNode): PNode =
if a != firstOrd(n.typ):
result = newIntNodeT(a, n)
proc foldMod*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64:
result = newIntNodeT(a mod b, n)
proc foldModU*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64:
result = newIntNodeT(a %% b, n)
proc foldDiv*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64):
result = newIntNodeT(a div b, n)
proc foldDivU*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64:
result = newIntNodeT(a /% b, n)
proc foldMul*(a, b: BiggestInt, n: PNode): PNode =
let res = a *% b
let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
let resAsFloat = toBiggestFloat(res)
# Fast path for normal case: small multiplicands, and no info
# is lost in either method.
if resAsFloat == floatProd and checkInRange(n, res):
return newIntNodeT(res, n)
# Somebody somewhere lost info. Close enough, or way off? Note
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
# The difference either is or isn't significant compared to the
# true value (of which floatProd is a good approximation).
# abs(diff)/abs(prod) <= 1/32 iff
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
checkInRange(n, res):
return newIntNodeT(res, n)
# implementation # implementation
proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode = proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
@ -172,23 +229,22 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat: of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n) result = newFloatNodeT(toFloat(int(getInt(a))), n)
# XXX: Hides overflow/underflow
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI: of mAbsI: result = foldAbs(getInt(a), n)
if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64 # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n) result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n) of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n) of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n) of mUnaryLt: result = foldSub(getOrdValue(a), 1, n)
of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n) of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n)
of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n) of mPred: result = foldSub(getOrdValue(a), getInt(b), n)
of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n) of mAddI: result = foldAdd(getInt(a), getInt(b), n)
of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n) of mSubI: result = foldSub(getInt(a), getInt(b), n)
of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n) of mMulI: result = foldMul(getInt(a), getInt(b), n)
of mMinI: of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(a), n)
@ -211,14 +267,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of tyInt64, tyInt, tyUInt..tyUInt64: of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shr") else: internalError(n.info, "constant folding for shr")
of mDivI: of mDivI: result = foldDiv(getInt(a), getInt(b), n)
let y = getInt(b) of mModI: result = foldMod(getInt(a), getInt(b), n)
if y != 0:
result = newIntNodeT(`|div|`(getInt(a), y), n)
of mModI:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`|mod|`(getInt(a), y), n)
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
@ -258,14 +308,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
of mModU: of mModU: result = foldModU(getInt(a), getInt(b), n)
let y = getInt(b) of mDivU: result = foldDivU(getInt(a), getInt(b), n)
if y != 0:
result = newIntNodeT(`%%`(getInt(a), y), n)
of mDivU:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`/%`(getInt(a), y), n)
of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n) of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n) of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
of mLtSet: of mLtSet:

View file

@ -612,13 +612,13 @@ proc firstOrd*(t: PType): BiggestInt =
else: else:
assert(t.n.sons[0].kind == nkSym) assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
result = firstOrd(lastSon(t)) result = firstOrd(lastSon(t))
of tyOrdinal: of tyOrdinal:
if t.len > 0: result = firstOrd(lastSon(t)) if t.len > 0: result = firstOrd(lastSon(t))
else: internalError("invalid kind for first(" & $t.kind & ')') else: internalError("invalid kind for firstOrd(" & $t.kind & ')')
else: else:
internalError("invalid kind for first(" & $t.kind & ')') internalError("invalid kind for firstOrd(" & $t.kind & ')')
result = 0 result = 0
proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
@ -651,14 +651,14 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
of tyEnum: of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
result = lastOrd(lastSon(t)) result = lastOrd(lastSon(t))
of tyProxy: result = 0 of tyProxy: result = 0
of tyOrdinal: of tyOrdinal:
if t.len > 0: result = lastOrd(lastSon(t)) if t.len > 0: result = lastOrd(lastSon(t))
else: internalError("invalid kind for last(" & $t.kind & ')') else: internalError("invalid kind for lastOrd(" & $t.kind & ')')
else: else:
internalError("invalid kind for last(" & $t.kind & ')') internalError("invalid kind for lastOrd(" & $t.kind & ')')
result = 0 result = 0
proc lengthOrd*(t: PType): BiggestInt = proc lengthOrd*(t: PType): BiggestInt =

23
tests/misc/tsemfold.nim Normal file
View file

@ -0,0 +1,23 @@
discard """
action: run
"""
doAssertRaises(OverflowError): discard low(int8) - 1'i8
doAssertRaises(OverflowError): discard high(int8) + 1'i8
doAssertRaises(OverflowError): discard abs(low(int8))
doAssertRaises(DivByZeroError): discard 1 mod 0
doAssertRaises(DivByZeroError): discard 1 div 0
doAssertRaises(OverflowError): discard low(int8) div -1'i8
doAssertRaises(OverflowError): discard low(int64) - 1'i64
doAssertRaises(OverflowError): discard high(int64) + 1'i64
type E = enum eA, eB
doAssertRaises(OverflowError): discard eA.pred
doAssertRaises(OverflowError): discard eB.succ
doAssertRaises(OverflowError): discard low(int8) * -1
doAssertRaises(OverflowError): discard low(int64) * -1
doAssertRaises(OverflowError): discard high(int8) * 2
doAssertRaises(OverflowError): discard high(int64) * 2

View file

@ -1,5 +1,5 @@
discard """ discard """
errormsg: "internal error: invalid kind for last(tyEmpty)" errormsg: "internal error: invalid kind for lastOrd(tyEmpty)"
""" """
var q = false var q = false
discard (if q: {} else: {}) discard (if q: {} else: {})