conversions to unsigned numbers are not checked anymore; implements /… (#12688) [backport]

* conversions to unsigned numbers are not checked anymore; implements / fixes https://github.com/nim-lang/RFCs/issues/175

* change the spec yet again to be less consistent but to make more sense; updated the changelog
This commit is contained in:
Andreas Rumpf 2019-11-20 17:08:43 +01:00 • committed by GitHub
commit c98e0e22ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 29 additions and 6 deletions

View file

@ -64,6 +64,9 @@
## Language changes ## Language changes
- Unsigned integer operators have been fixed to allow promotion of the first operand. - 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 ### Tool changes

View file

@ -1929,7 +1929,8 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
var a: TLoc var a: TLoc
var dest = skipTypes(n.typ, abstractVar) 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) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, n, "(($1) ($2))" % putIntoDest(p, d, n, "(($1) ($2))" %
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)

View file

@ -2155,7 +2155,10 @@ proc upConv(p: PProc, n: PNode, r: var TCompRes) =
proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
var a, b: TCompRes var a, b: TCompRes
gen(p, n.sons[0], r) 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[1], a)
gen(p, n.sons[2], b) gen(p, n.sons[2], b)
useMagic(p, "chckRange") useMagic(p, "chckRange")

View file

@ -146,6 +146,10 @@ type
## Allows to modify a NimNode where the type has already been ## Allows to modify a NimNode where the type has already been
## flagged with nfSem. If you actually do this, it will cause ## flagged with nfSem. If you actually do this, it will cause
## bugs. ## 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 SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf disabledSf, writeOnlySf, readOnlySf, v2Sf

View file

@ -434,15 +434,12 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc}) let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc})
let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc}) let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc})
# if srcTyp.kind == tyUInt64 and "FFFFFF" in $n: # if srcTyp.kind == tyUInt64 and "FFFFFF" in $n:
# echo "n: ", n, " a: ", a # echo "n: ", n, " a: ", a
# echo "from: ", srcTyp, " to: ", dstTyp, " check: ", check # echo "from: ", srcTyp, " to: ", dstTyp, " check: ", check
# echo getInt(a) # echo getInt(a)
# echo high(int64) # echo high(int64)
# writeStackTrace() # writeStackTrace()
# XXX range checks?
case dstTyp.kind case dstTyp.kind
of tyInt..tyInt64, tyUInt..tyUInt64: of tyInt..tyInt64, tyUInt..tyUInt64:
case srcTyp.kind case srcTyp.kind
@ -450,6 +447,7 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
result = newIntNodeT(toInt128(getFloat(a)), n, g) result = newIntNodeT(toInt128(getFloat(a)), n, g)
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64: of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
var val = a.getOrdValue var val = a.getOrdValue
if check: rangeCheck(n, val, g) if check: rangeCheck(n, val, g)
result = newIntNodeT(val, n, g) result = newIntNodeT(val, n, g)
if dstTyp.kind in {tyUInt..tyUInt64}: if dstTyp.kind in {tyUInt..tyUInt64}:

View file

@ -3188,6 +3188,7 @@ has lots of advantages:
Type conversions Type conversions
---------------- ----------------
Syntactically a `type conversion` is like a procedure call, but a Syntactically a `type conversion` is like a procedure call, but a
type name replaces the procedure name. A type conversion is always type name replaces the procedure name. A type conversion is always
safe in the sense that a failure to convert a type to another 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) let procVar = (proc(x: string))(p)
procVar("a") 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 Type casts
---------- ----------