breaking change: 'and' and 'mod' do not produce a subrange type anymore; fixes #5854

This commit is contained in:
Andreas Rumpf
2017-10-30 00:27:30 +01:00
parent aa526da706
commit d7a896f19d
6 changed files with 16 additions and 131 deletions

View File

@@ -28,3 +28,6 @@
take ``BackwardsIndex`` indices. ``BackwardsIndex`` is produced by ``system.^``.
This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
with ``system.BackwardsIndex`` (if applicable for the accessors).
- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This
turned out to be more harmful than helpful and the language is simpler
without this special typing rule.

View File

@@ -550,7 +550,6 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
result = semfold.getConstExpr(c.module, call)
if result.isNil: result = n
else: return result
result.typ = semfold.getIntervalType(callee.magic, call)
block maybeLabelAsStatic:
# XXX: temporary work-around needed for tlateboundstatic.

View File

@@ -137,116 +137,6 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
result.n = n
addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
proc getIntervalType*(m: TMagic, n: PNode): PType =
# Nim requires interval arithmetic for ``range`` types. Lots of tedious
# work but the feature is very nice for reducing explicit conversions.
const ordIntLit = {nkIntLit..nkUInt64Lit}
result = n.typ
template commutativeOp(opr: untyped) =
let a = n.sons[1]
let b = n.sons[2]
if isIntRangeOrLit(a.typ) and isIntRangeOrLit(b.typ):
result = makeRange(pickIntRange(a.typ, b.typ),
opr(pickMinInt(a), pickMinInt(b)),
opr(pickMaxInt(a), pickMaxInt(b)))
template binaryOp(opr: untyped) =
let a = n.sons[1]
let b = n.sons[2]
if isIntRange(a.typ) and b.kind in {nkIntLit..nkUInt64Lit}:
result = makeRange(a.typ,
opr(pickMinInt(a), pickMinInt(b)),
opr(pickMaxInt(a), pickMaxInt(b)))
case m
of mUnaryMinusI, mUnaryMinusI64:
let a = n.sons[1].typ
if isIntRange(a):
# (1..3) * (-1) == (-3.. -1)
result = makeRange(a, 0|-|lastOrd(a), 0|-|firstOrd(a))
of mUnaryMinusF64:
let a = n.sons[1].typ
if isFloatRange(a):
result = makeRangeF(a, -getFloat(a.n.sons[1]),
-getFloat(a.n.sons[0]))
of mAbsF64:
let a = n.sons[1].typ
if isFloatRange(a):
# abs(-5.. 1) == (1..5)
if a.n[0].floatVal <= 0.0:
result = makeRangeF(a, 0.0, abs(getFloat(a.n.sons[0])))
else:
result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
abs(getFloat(a.n.sons[0])))
of mAbsI:
let a = n.sons[1].typ
if isIntRange(a):
if a.n[0].intVal <= 0:
result = makeRange(a, 0, `|abs|`(getInt(a.n.sons[0])))
else:
result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
`|abs|`(getInt(a.n.sons[0])))
of mSucc:
let a = n.sons[1].typ
let b = n.sons[2].typ
if isIntRange(a) and isIntLit(b):
# (-5.. 1) + 6 == (-5 + 6)..(-1 + 6)
result = makeRange(a, pickMinInt(n.sons[1]) |+| pickMinInt(n.sons[2]),
pickMaxInt(n.sons[1]) |+| pickMaxInt(n.sons[2]))
of mPred:
let a = n.sons[1].typ
let b = n.sons[2].typ
if isIntRange(a) and isIntLit(b):
result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]),
pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2]))
of mAddI, mAddU:
commutativeOp(`|+|`)
of mMulI, mMulU:
commutativeOp(`|*|`)
of mSubI, mSubU:
binaryOp(`|-|`)
of mBitandI:
# since uint64 is still not even valid for 'range' (since it's no ordinal
# yet), we exclude it from the list (see bug #1638) for now:
var a = n.sons[1]
var b = n.sons[2]
# symmetrical:
if b.kind notin ordIntLit: swap(a, b)
if b.kind in ordIntLit:
let x = b.intVal|+|1
if (x and -x) == x and x >= 0:
result = makeRange(n.typ, 0, b.intVal)
of mModU:
let a = n.sons[1]
let b = n.sons[2]
if b.kind in ordIntLit:
if b.intVal >= 0:
result = makeRange(n.typ, 0, b.intVal-1)
else:
result = makeRange(n.typ, b.intVal+1, 0)
of mModI:
# so ... if you ever wondered about modulo's signedness; this defines it:
let a = n.sons[1]
let b = n.sons[2]
if b.kind in {nkIntLit..nkUInt64Lit}:
if b.intVal >= 0:
result = makeRange(n.typ, -(b.intVal-1), b.intVal-1)
else:
result = makeRange(n.typ, b.intVal+1, -(b.intVal+1))
of mDivI, mDivU:
binaryOp(`|div|`)
of mMinI:
commutativeOp(min)
of mMaxI:
commutativeOp(max)
else: discard
discard """
mShlI,
mShrI, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
"""
proc evalIs(n, a: PNode): PNode =
# XXX: This should use the standard isOpImpl
internalAssert a.kind == nkSym and a.sym.kind == skType

View File

@@ -137,25 +137,6 @@ determined). Assignments from the base type to one of its subrange types
A subrange type has the same size as its base type (``int`` in the example).
Nim requires `interval arithmetic`:idx: for subrange types over a set
of built-in operators that involve constants: ``x %% 3`` is of
type ``range[0..2]``. The following built-in operators for integers are
affected by this rule: ``-``, ``+``, ``*``, ``min``, ``max``, ``succ``,
``pred``, ``mod``, ``div``, ``%%``, ``and`` (bitwise ``and``).
Bitwise ``and`` only produces a ``range`` if one of its operands is a
constant *x* so that (x+1) is a power of two.
(Bitwise ``and`` is then a ``%%`` operation.)
This means that the following code is accepted:
.. code-block:: nim
case (x and 3) + 7
of 7: echo "A"
of 8: echo "B"
of 9: echo "C"
of 10: echo "D"
# note: no ``else`` required as (x and 3) + 7 has the type: range[7..10]
Pre-defined floating point types

View File

@@ -0,0 +1,13 @@
discard """
output: '''1'''
"""
# bug #5854
type
n16* = range[0'i16..high(int16)]
var level: n16 = 1
let maxLevel: n16 = 1
level = min(level + 2, maxLevel)
echo level

View File

@@ -2,7 +2,6 @@ version 1.0 battle plan
=======================
- make FlowVar compatible to Futures
- remove 'mod x' type rule
- fix "high priority" bugs
- try to fix as many compiler crashes as reasonable