Merge pull request #7736 from cooldome/range_float_type
Language feature: range float types
This commit is contained in:
commit
d8f7174dda
6 changed files with 118 additions and 26 deletions
|
|
@ -153,6 +153,8 @@
|
||||||
|
|
||||||
- Nim now supports `except` clause in the export statement.
|
- Nim now supports `except` clause in the export statement.
|
||||||
|
|
||||||
|
- Range float types, example `range[0.0 .. Inf]`. More details in language manual.
|
||||||
|
|
||||||
### Tool changes
|
### Tool changes
|
||||||
|
|
||||||
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
|
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
|
||||||
|
|
|
||||||
|
|
@ -238,8 +238,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if not hasUnknownTypes:
|
if not hasUnknownTypes:
|
||||||
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
|
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
|
||||||
localError(c.config, n.info, "type mismatch")
|
localError(c.config, n.info, "type mismatch")
|
||||||
elif not rangeT[0].isOrdinalType:
|
elif not rangeT[0].isOrdinalType and rangeT[0].kind notin tyFloat..tyFloat128:
|
||||||
localError(c.config, n.info, "ordinal type expected")
|
localError(c.config, n.info, "ordinal or float type expected")
|
||||||
elif enumHasHoles(rangeT[0]):
|
elif enumHasHoles(rangeT[0]):
|
||||||
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -637,20 +637,22 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||||
let
|
template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
|
||||||
a0 = firstOrd(nil, a)
|
if a0 == f0 and a1 == f1:
|
||||||
a1 = lastOrd(nil, a)
|
isEqual
|
||||||
f0 = firstOrd(nil, f)
|
elif a0 >= f0 and a1 <= f1:
|
||||||
f1 = lastOrd(nil, f)
|
isConvertible
|
||||||
if a0 == f0 and a1 == f1:
|
elif a0 <= f1 and f0 <= a1:
|
||||||
result = isEqual
|
# X..Y and C..D overlap iff (X <= D and C <= Y)
|
||||||
elif a0 >= f0 and a1 <= f1:
|
isConvertible
|
||||||
result = isConvertible
|
else:
|
||||||
elif a0 <= f1 and f0 <= a1:
|
isNone
|
||||||
# X..Y and C..D overlap iff (X <= D and C <= Y)
|
|
||||||
result = isConvertible
|
if f.isOrdinalType:
|
||||||
else:
|
checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
|
||||||
result = isNone
|
else:
|
||||||
|
checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
|
||||||
|
|
||||||
|
|
||||||
proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
||||||
var
|
var
|
||||||
|
|
|
||||||
|
|
@ -89,13 +89,16 @@ proc isPureObject*(typ: PType): bool =
|
||||||
|
|
||||||
proc getOrdValue*(n: PNode): BiggestInt =
|
proc getOrdValue*(n: PNode): BiggestInt =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCharLit..nkUInt64Lit: result = n.intVal
|
of nkCharLit..nkUInt64Lit: n.intVal
|
||||||
of nkNilLit: result = 0
|
of nkNilLit: 0
|
||||||
of nkHiddenStdConv: result = getOrdValue(n.sons[1])
|
of nkHiddenStdConv: getOrdValue(n.sons[1])
|
||||||
else:
|
else: high(BiggestInt)
|
||||||
#localError(n.info, errOrdinalTypeExpected)
|
|
||||||
# XXX check usages of getOrdValue
|
proc getFloatValue*(n: PNode): BiggestFloat =
|
||||||
result = high(BiggestInt)
|
case n.kind
|
||||||
|
of nkFloatLiterals: n.floatVal
|
||||||
|
of nkHiddenStdConv: getFloatValue(n.sons[1])
|
||||||
|
else: NaN
|
||||||
|
|
||||||
proc isIntLit*(t: PType): bool {.inline.} =
|
proc isIntLit*(t: PType): bool {.inline.} =
|
||||||
result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit
|
result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit
|
||||||
|
|
@ -593,6 +596,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
result = typeToStr[t.kind]
|
result = typeToStr[t.kind]
|
||||||
result.addTypeFlags(t)
|
result.addTypeFlags(t)
|
||||||
|
|
||||||
|
|
||||||
proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
|
proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
|
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
|
||||||
|
|
@ -627,6 +631,21 @@ proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
|
||||||
internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
|
internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
|
|
||||||
|
proc firstFloat*(t: PType): BiggestFloat =
|
||||||
|
case t.kind
|
||||||
|
of tyFloat..tyFloat128: -Inf
|
||||||
|
of tyRange:
|
||||||
|
assert(t.n != nil) # range directly given:
|
||||||
|
assert(t.n.kind == nkRange)
|
||||||
|
getFloatValue(t.n.sons[0])
|
||||||
|
of tyVar: firstFloat(t.sons[0])
|
||||||
|
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
|
||||||
|
firstFloat(lastSon(t))
|
||||||
|
else:
|
||||||
|
internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')')
|
||||||
|
NaN
|
||||||
|
|
||||||
proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt =
|
proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt =
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyBool: result = 1
|
of tyBool: result = 1
|
||||||
|
|
@ -667,6 +686,22 @@ proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt =
|
||||||
internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
|
internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
|
|
||||||
|
proc lastFloat*(t: PType): BiggestFloat =
|
||||||
|
case t.kind
|
||||||
|
of tyFloat..tyFloat128: Inf
|
||||||
|
of tyVar: lastFloat(t.sons[0])
|
||||||
|
of tyRange:
|
||||||
|
assert(t.n != nil) # range directly given:
|
||||||
|
assert(t.n.kind == nkRange)
|
||||||
|
getFloatValue(t.n.sons[1])
|
||||||
|
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
|
||||||
|
lastFloat(lastSon(t))
|
||||||
|
else:
|
||||||
|
internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')')
|
||||||
|
NaN
|
||||||
|
|
||||||
|
|
||||||
proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
|
proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
|
of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
|
||||||
|
|
|
||||||
|
|
@ -741,22 +741,26 @@ For further details, see `Convertible relation
|
||||||
|
|
||||||
Subrange types
|
Subrange types
|
||||||
--------------
|
--------------
|
||||||
A subrange type is a range of values from an ordinal type (the base
|
A subrange type is a range of values from an ordinal or floating point type (the base
|
||||||
type). To define a subrange type, one must specify it's limiting values: the
|
type). To define a subrange type, one must specify it's limiting values: the
|
||||||
lowest and highest value of the type:
|
lowest and highest value of the type:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
Subrange = range[0..5]
|
Subrange = range[0..5]
|
||||||
|
PositiveFloat = range[0.0..Inf]
|
||||||
|
|
||||||
|
|
||||||
``Subrange`` is a subrange of an integer which can only hold the values 0
|
``Subrange`` is a subrange of an integer which can only hold the values 0
|
||||||
to 5. Assigning any other value to a variable of type ``Subrange`` is a
|
to 5. ``PositiveFloat`` defines a subrange of all positive floating point values.
|
||||||
|
NaN does not belong to any subrange of floating point types.
|
||||||
|
Assigning any other value to a variable of type ``Subrange`` is a
|
||||||
checked runtime error (or static error if it can be statically
|
checked runtime error (or static error if it can be statically
|
||||||
determined). Assignments from the base type to one of its subrange types
|
determined). Assignments from the base type to one of its subrange types
|
||||||
(and vice versa) are allowed.
|
(and vice versa) are allowed.
|
||||||
|
|
||||||
A subrange type has the same size as its base type (``int`` in the example).
|
A subrange type has the same size as its base type (``int`` in the
|
||||||
|
Subrange example).
|
||||||
|
|
||||||
|
|
||||||
Pre-defined floating point types
|
Pre-defined floating point types
|
||||||
|
|
|
||||||
49
tests/float/tfloatrange.nim
Normal file
49
tests/float/tfloatrange.nim
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c -d:release --rangeChecks:on $file"
|
||||||
|
output: '''StrictPositiveRange
|
||||||
|
float
|
||||||
|
range fail expected
|
||||||
|
range fail expected
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
import math, fenv
|
||||||
|
|
||||||
|
type
|
||||||
|
Positive = range[0.0..Inf]
|
||||||
|
StrictPositive = range[minimumPositiveValue(float)..Inf]
|
||||||
|
Negative32 = range[-maximumPositiveValue(float32) .. -1.0'f32]
|
||||||
|
|
||||||
|
proc myoverload(x: float) =
|
||||||
|
echo "float"
|
||||||
|
|
||||||
|
proc myoverload(x: Positive) =
|
||||||
|
echo "PositiveRange"
|
||||||
|
|
||||||
|
proc myoverload(x: StrictPositive) =
|
||||||
|
echo "StrictPositiveRange"
|
||||||
|
|
||||||
|
let x = 9.0.StrictPositive
|
||||||
|
myoverload(x)
|
||||||
|
myoverload(9.0)
|
||||||
|
|
||||||
|
doAssert(sqrt(x) == 3.0)
|
||||||
|
|
||||||
|
var z = -10.0
|
||||||
|
try:
|
||||||
|
myoverload(StrictPositive(z))
|
||||||
|
except:
|
||||||
|
echo "range fail expected"
|
||||||
|
|
||||||
|
|
||||||
|
proc strictOnlyProc(x: StrictPositive): bool =
|
||||||
|
if x > 1.0: true else: false
|
||||||
|
|
||||||
|
let x2 = 5.0.Positive
|
||||||
|
doAssert(strictOnlyProc(x2))
|
||||||
|
|
||||||
|
try:
|
||||||
|
let x4 = 0.0.Positive
|
||||||
|
discard strictOnlyProc(x4)
|
||||||
|
except:
|
||||||
|
echo "range fail expected"
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue