summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md3
-rw-r--r--compiler/ccgexprs.nim3
-rw-r--r--compiler/jsgen.nim5
-rw-r--r--compiler/options.nim4
-rw-r--r--compiler/semfold.nim6
-rw-r--r--doc/manual.rst14
6 files changed, 29 insertions, 6 deletions
diff --git a/changelog.md b/changelog.md
index 4aeb51141..caead21b4 100644
--- a/changelog.md
+++ b/changelog.md
@@ -64,6 +64,9 @@
 ## Language changes
 
 - Unsigned integer operators have been fixed to allow promotion of the first operand.
+- Conversions to unsigned integers are unchecked at runtime, imitating earlier Nim
+  versions. The documentation was improved to acknowledge this special case.
+  See https://github.com/nim-lang/RFCs/issues/175 for more details.
 
 
 ### Tool changes
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 1d9d0aca6..009ee7221 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1929,7 +1929,8 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
 proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
   var a: TLoc
   var dest = skipTypes(n.typ, abstractVar)
-  if optRangeCheck notin p.options:
+  if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
+      checkUnsignedConversions notin p.config.legacyFeatures):
     initLocExpr(p, n.sons[0], a)
     putIntoDest(p, d, n, "(($1) ($2))" %
         [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 4b3862cff..f4d43ce0c 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -2155,7 +2155,10 @@ proc upConv(p: PProc, n: PNode, r: var TCompRes) =
 proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
   var a, b: TCompRes
   gen(p, n.sons[0], r)
-  if optRangeCheck in p.options:
+  if optRangeCheck notin p.options or (skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64} and
+      checkUnsignedConversions notin p.config.legacyFeatures):
+    discard "XXX maybe emit masking instructions here"
+  else:
     gen(p, n.sons[1], a)
     gen(p, n.sons[2], b)
     useMagic(p, "chckRange")
diff --git a/compiler/options.nim b/compiler/options.nim
index 50bae9557..52ba8254a 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -146,6 +146,10 @@ type
       ## Allows to modify a NimNode where the type has already been
       ## flagged with nfSem. If you actually do this, it will cause
       ## bugs.
+    checkUnsignedConversions
+      ## Historically and especially in version 1.0.0 of the language
+      ## conversions to unsigned numbers were checked. In 1.0.4 they
+      ## are not anymore.
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index ec3ebd65b..ec2d646c8 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -434,15 +434,12 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
   let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc})
   let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc})
 
-
   # if srcTyp.kind == tyUInt64 and "FFFFFF" in $n:
   #   echo "n: ", n, " a: ", a
   #   echo "from: ", srcTyp, " to: ", dstTyp, " check: ", check
   #   echo getInt(a)
   #   echo high(int64)
   #   writeStackTrace()
-
-  # XXX range checks?
   case dstTyp.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
     case srcTyp.kind
@@ -450,9 +447,10 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
       result = newIntNodeT(toInt128(getFloat(a)), n, g)
     of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
       var val = a.getOrdValue
+
       if check: rangeCheck(n, val, g)
       result = newIntNodeT(val, n, g)
-      if dstTyp.kind in {tyUInt .. tyUInt64}:
+      if dstTyp.kind in {tyUInt..tyUInt64}:
         result.kind = nkUIntLit
     else:
       result = a
diff --git a/doc/manual.rst b/doc/manual.rst
index c869b391f..82487a385 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -3188,6 +3188,7 @@ has lots of advantages:
 
 Type conversions
 ----------------
+
 Syntactically a `type conversion` is like a procedure call, but a
 type name replaces the procedure name. A type conversion is always
 safe in the sense that a failure to convert a type to another
@@ -3207,6 +3208,19 @@ A type conversion can also be used to disambiguate overloaded routines:
   let procVar = (proc(x: string))(p)
   procVar("a")
 
+Since operations on unsigned numbers wrap around and are unchecked so are
+type conversion to unsigned integers and between unsigned integers. The
+rationale for this is mostly better interoperability with the C Programming
+language when algorithms are ported from C to Nim.
+
+Exception: Values that are converted to an unsigned type at compile time
+are checked so that code like ``byte(-1)`` does not compile.
+
+**Note**: Historically the operations
+were unchecked and the conversions were sometimes checked but starting with
+the revision 1.0.4 of this document and the language implementation the
+conversions too are now *always unchecked*.
+
 
 Type casts
 ----------