summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md2
-rw-r--r--compiler/semtypes.nim4
-rw-r--r--compiler/sigmatch.nim30
-rw-r--r--compiler/types.nim49
-rw-r--r--doc/manual.rst10
-rw-r--r--tests/float/tfloatrange.nim49
6 files changed, 118 insertions, 26 deletions
diff --git a/changelog.md b/changelog.md
index 31b31c5bf..3a905d0b9 100644
--- a/changelog.md
+++ b/changelog.md
@@ -153,6 +153,8 @@
 
 - Nim now supports `except` clause in the export statement.
 
+- Range float types, example `range[0.0 .. Inf]`. More details in language manual.
+
 ### Tool changes
 
 - ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index a0144500e..d1ebb950c 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -238,8 +238,8 @@ 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:
-      localError(c.config, n.info, "ordinal type expected")
+    elif not rangeT[0].isOrdinalType and rangeT[0].kind notin tyFloat..tyFloat128:
+      localError(c.config, n.info, "ordinal or float type expected")
     elif enumHasHoles(rangeT[0]):
       localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 784b5c11c..84e59349e 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -637,20 +637,22 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   else: discard
 
 proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
-  let
-    a0 = firstOrd(nil, a)
-    a1 = lastOrd(nil, a)
-    f0 = firstOrd(nil, f)
-    f1 = lastOrd(nil, f)
-  if a0 == f0 and a1 == f1:
-    result = isEqual
-  elif a0 >= f0 and a1 <= f1:
-    result = isConvertible
-  elif a0 <= f1 and f0 <= a1:
-    # X..Y and C..D overlap iff (X <= D and C <= Y)
-    result = isConvertible
-  else:
-    result = isNone
+  template checkRange[T](a0, a1, f0, f1: T): TTypeRelation = 
+    if a0 == f0 and a1 == f1:
+      isEqual
+    elif a0 >= f0 and a1 <= f1:
+      isConvertible
+    elif a0 <= f1 and f0 <= a1:
+      # X..Y and C..D overlap iff (X <= D and C <= Y)
+      isConvertible
+    else:
+      isNone
+  
+  if f.isOrdinalType: 
+    checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
+  else: 
+    checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
+    
 
 proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   var
diff --git a/compiler/types.nim b/compiler/types.nim
index f78e975ee..16a84dee6 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -89,13 +89,16 @@ proc isPureObject*(typ: PType): bool =
 
 proc getOrdValue*(n: PNode): BiggestInt =
   case n.kind
-  of nkCharLit..nkUInt64Lit: result = n.intVal
-  of nkNilLit: result = 0
-  of nkHiddenStdConv: result = getOrdValue(n.sons[1])
-  else:
-    #localError(n.info, errOrdinalTypeExpected)
-    # XXX check usages of getOrdValue
-    result = high(BiggestInt)
+  of nkCharLit..nkUInt64Lit: n.intVal
+  of nkNilLit: 0
+  of nkHiddenStdConv: getOrdValue(n.sons[1])
+  else: high(BiggestInt)
+
+proc getFloatValue*(n: PNode): BiggestFloat =
+  case n.kind
+  of nkFloatLiterals: n.floatVal
+  of nkHiddenStdConv: getFloatValue(n.sons[1])
+  else: NaN
 
 proc isIntLit*(t: PType): bool {.inline.} =
   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.addTypeFlags(t)
 
+
 proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
   case t.kind
   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 & ')')
     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 =
   case t.kind
   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 & ')')
     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 =
   case t.kind
   of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
diff --git a/doc/manual.rst b/doc/manual.rst
index c267c706f..ddee7b4b9 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -741,22 +741,26 @@ For further details, see `Convertible relation
 
 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
 lowest and highest value of the type:
 
 .. code-block:: nim
   type
     Subrange = range[0..5]
+    PositiveFloat = range[0.0..Inf]
 
 
 ``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
 determined). Assignments from the base type to one of its subrange types
 (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
diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim
new file mode 100644
index 000000000..e8ea1912e
--- /dev/null
+++ b/tests/float/tfloatrange.nim
@@ -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"
+  
\ No newline at end of file