implement RFCs/294 ; disallow enum <=> enum conversion (#16351)

* fix https://github.com/nim-lang/RFCs/issues/294 ; disallow enum <=> enum conversion
* fix the runnableExamples that was the instigator of this RFC
* legacy -d:nimLegacyConvEnumEnum
* use -d:nimLegacyConvEnumEnum in important_package nimgame2
* add test for enum cast
* improve changelog
* add changelog: Changes affecting backward compatibility
* cleanup changelog
* fix changelog
This commit is contained in:
Timothee Cour 2021-04-02 22:15:21 -07:00 • committed by GitHub
commit 270964c487
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 115 additions and 38 deletions

View file

@ -2,6 +2,40 @@
## Changes affecting backward compatibility
- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent,
see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior.
- An enum now can't be converted to another enum directly, you must use `ord` (or `cast`, but
compiler won't help if you misuse it).
```
type A = enum a1, a2
type B = enum b1, b2
doAssert not compiles(a1.B)
doAssert compiles(a1.ord.B)
```
for a transition period, use `-d:nimLegacyConvEnumEnum`.
- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous
behavior.
- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior
silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior.
- `math.round` now is rounded "away from zero" in JS backend which is consistent
with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior.
- Changed the behavior of `uri.decodeQuery` when there are unencoded `=`
characters in the decoded values. Prior versions would raise an error. This is
no longer the case to comply with the HTML spec and other languages
implementations. Old behavior can be obtained with
`-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same
underlying code is also updated the same way.
- In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`,
unless `-d:nimLegacyHomeDir` is specified (for a transition period).
## Standard library additions and changes ## Standard library additions and changes
- Added `sections` iterator in `parsecfg`. - Added `sections` iterator in `parsecfg`.
@ -84,9 +118,6 @@
- Added a simpler to use `io.readChars` overload. - Added a simpler to use `io.readChars` overload.
- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent,
see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior.
- Added `**` to jsffi. - Added `**` to jsffi.
- `writeStackTrace` is available in JS backend now. - `writeStackTrace` is available in JS backend now.
@ -109,9 +140,6 @@
- Added `math.isNaN`. - Added `math.isNaN`.
- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior
silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior.
- Added `jsbigints` module, arbitrary precision integers for JavaScript target. - Added `jsbigints` module, arbitrary precision integers for JavaScript target.
- Added `math.copySign`. - Added `math.copySign`.
@ -130,18 +158,8 @@
- Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs. - Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs.
https://www.freedesktop.org/software/systemd/man/os-release.html https://www.freedesktop.org/software/systemd/man/os-release.html
- `math.round` now is rounded "away from zero" in JS backend which is consistent
with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior.
- Added `socketstream` module that wraps sockets in the stream interface - Added `socketstream` module that wraps sockets in the stream interface
- Changed the behavior of `uri.decodeQuery` when there are unencoded `=`
characters in the decoded values. Prior versions would raise an error. This is
no longer the case to comply with the HTML spec and other languages
implementations. Old behavior can be obtained with
`-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same
underlying code is also updated the same way.
- Added `sugar.dumpToString` which improves on `sugar.dump`. - Added `sugar.dumpToString` which improves on `sugar.dump`.
- Added `math.signbit`. - Added `math.signbit`.
@ -235,7 +253,6 @@
- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger). - Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger).
- Added `std/channels`. - Added `std/channels`.
- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals). - Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals).
@ -243,9 +260,6 @@
- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time - Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time
zone offsets without colons, e.g. `UTC+7 -> +0700`. zone offsets without colons, e.g. `UTC+7 -> +0700`.
- In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`,
unless `-d:nimLegacyHomeDir` is specified (for a transition period).
- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. - Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.
- Added dollar `$` and `len` for `jsre.RegExp`. - Added dollar `$` and `len` for `jsre.RegExp`.
@ -254,7 +268,6 @@
- Added `hasClosure` to `std/typetraits`. - Added `hasClosure` to `std/typetraits`.
- Added `genasts.genAst` that avoids the problems inherent with `quote do` and can - Added `genasts.genAst` that avoids the problems inherent with `quote do` and can
be used as a replacement. be used as a replacement.
@ -304,9 +317,6 @@
- VM now supports `addr(mystring[ind])` (index + index assignment) - VM now supports `addr(mystring[ind])` (index + index assignment)
- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous
behavior.
- Added `--hintAsError` with similar semantics as `--warningAsError`. - Added `--hintAsError` with similar semantics as `--warningAsError`.
- TLS: OSX now uses native TLS (`--tlsEmulation:off`), TLS now works with importcpp non-POD types, - TLS: OSX now uses native TLS (`--tlsEmulation:off`), TLS now works with importcpp non-POD types,

View file

@ -3,10 +3,14 @@
This is an example file. This is an example file.
The changes should go to changelog.md! The changes should go to changelog.md!
## Changes affecting backward compatibility
- `foo` now behaves differently, use `-d:nimLegacyFoo` for previous behavior.
## Standard library additions and changes ## Standard library additions and changes
- Added `example.exampleProc`. - Added `example.exampleProc`.
- Changed `example.foo` to take additional `bar` parameter. - Changed `example.foo` to take additional `bar` parameter.

View file

@ -163,6 +163,11 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers) result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers)
elif (targetBaseTyp.kind in IntegralTypes) and elif (targetBaseTyp.kind in IntegralTypes) and
(srcBaseTyp.kind in IntegralTypes): (srcBaseTyp.kind in IntegralTypes):
if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum:
if c.config.isDefined("nimLegacyConvEnumEnum"):
message(c.config, src.info, warnUser, "enum to enum conversion is now deprecated")
else: result = convNotLegal
# `elif` would be incorrect here
if targetTyp.kind == tyBool: if targetTyp.kind == tyBool:
discard "convOk" discard "convOk"
elif targetTyp.isOrdinalType: elif targetTyp.isOrdinalType:

View file

@ -137,7 +137,7 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
# we allow accesses nevertheless in top level statements for # we allow accesses nevertheless in top level statements for
# easier initialization: # easier initialization:
#if a.isTopLevel: #if a.isTopLevel:
# message(n.info, warnUnguardedAccess, renderTree(n)) # message(a.config, n.info, warnUnguardedAccess, renderTree(n))
#else: #else:
if not a.isTopLevel: if not a.isTopLevel:
localError(a.config, n.info, "unguarded access: " & renderTree(n)) localError(a.config, n.info, "unguarded access: " & renderTree(n))
@ -478,7 +478,7 @@ proc getLockLevel(s: PSym): TLockLevel =
result = 0.TLockLevel result = 0.TLockLevel
else: else:
result = UnknownLockLevel result = UnknownLockLevel
#message(s.info, warnUser, "FOR THIS " & s.name.s) #message(??.config, s.info, warnUser, "FOR THIS " & s.name.s)
proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) = proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
if lockLevel >= tracked.currLockLevel: if lockLevel >= tracked.currLockLevel:
@ -544,7 +544,7 @@ proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
else: op.lockLevel else: op.lockLevel
#if lockLevel == UnknownLockLevel: #if lockLevel == UnknownLockLevel:
# message(n.info, warnUser, "had to assume the worst here") # message(??.config, n.info, warnUser, "had to assume the worst here")
mergeLockLevels(tracked, n, lockLevel) mergeLockLevels(tracked, n, lockLevel)
proc isOwnedProcVar(n: PNode; owner: PSym): bool = proc isOwnedProcVar(n: PNode; owner: PSym): bool =

View file

@ -1435,7 +1435,7 @@ proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
# permanent, so that it's not used for anything else: # permanent, so that it's not used for anything else:
c.prc.slots[tmp].kind = slotTempPerm c.prc.slots[tmp].kind = slotTempPerm
# XXX this is still a hack # XXX this is still a hack
#message(n.info, warnUser, "suspicious opcode used") #message(c.congig, n.info, warnUser, "suspicious opcode used")
else: else:
gABC(c, n, opcAddrReg, dest, tmp) gABC(c, n, opcAddrReg, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
@ -1687,7 +1687,7 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
c.gABC(n, opcNodeToReg, dest, cc) c.gABC(n, opcNodeToReg, dest, cc)
c.freeTemp(cc) c.freeTemp(cc)
else: else:
#message(n.info, warnUser, "argh") #message(c.config, n.info, warnUser, "argh")
#echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ) #echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ)
c.gABC(n, opc, dest, a, b) c.gABC(n, opc, dest, a, b)
c.freeTemp(a) c.freeTemp(a)

View file

@ -9,7 +9,7 @@ proc `==`*[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} =
place1, place2 = 3 place1, place2 = 3
var var
e1 = field1 e1 = field1
e2 = Enum1(place2) e2 = place2.ord.Enum1
assert e1 == e2 assert e1 == e2
assert not compiles(e1 == place2) # raises error assert not compiles(e1 == place2) # raises error
proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} = proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} =

View file

@ -96,14 +96,15 @@ pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround need
pkg "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg "NimData", "nim c -o:nimdataa src/nimdata.nim"
pkg "nimes", "nim c src/nimes.nim" pkg "nimes", "nim c src/nimes.nim"
pkg "nimfp", "nim c -o:nfp -r src/fp.nim" pkg "nimfp", "nim c -o:nfp -r src/fp.nim"
pkg "nimgame2", "nim c nimgame2/nimgame.nim", allowFailure = true # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgame2", "nim c -d:nimLegacyConvEnumEnum nimgame2/nimgame.nim", allowFailure = true
# XXX Doesn't work with deprecated 'randomize', will create a PR.
pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim"
pkg "nimlsp" pkg "nimlsp"
pkg "nimly", "nim c -r tests/test_readme_example.nim" pkg "nimly", "nim c -r tests/test_readme_example.nim"
pkg "nimongo", "nimble test_ci", allowFailure = true pkg "nimongo", "nimble test_ci", allowFailure = true
pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true
pkg "nimpy", "nim c -r tests/nimfrompy.nim" pkg "nimpy", "nim c -r tests/nimfrompy.nim"
pkg "nimquery" pkg "nimquery", allowFailure = true # pending https://github.com/GULPF/nimquery/pull/10
pkg "nimsl" pkg "nimsl"
pkg "nimsvg" pkg "nimsvg"
pkg "nimterop", "nimble minitest" pkg "nimterop", "nimble minitest"

View file

@ -48,7 +48,7 @@ block:
var x: ref int = nil var x: ref int = nil
doAssert cast[int](cast[ptr int](x)) == 0 doAssert cast[int](cast[ptr int](x)) == 0
block: block: # cast of nil
block: block:
static: static:
let a = cast[pointer](nil) let a = cast[pointer](nil)
@ -71,7 +71,33 @@ block:
static: static:
doAssert cast[RootRef](nil).repr == "nil" doAssert cast[RootRef](nil).repr == "nil"
# Issue #15730, not fixed yet when false: # xxx bug #15730, not fixed yet
# block: block:
# static: static:
# doAssert cast[cstring](nil).repr == "nil" doAssert cast[cstring](nil).repr == "nil"
template main() =
# xxx move all under here to get tested in VM
block: # cast of enum
type Koo = enum k1, k2
type Goo = enum g1, g2
type Boo = enum b1 = -1, b2, b3, b4
type Coo = enum c1 = -1i8, c2, c3, c4
when nimvm:
# xxx: Error: VM does not support 'cast' from tyEnum to tyEnum
discard
else:
doAssert cast[Koo](k2) == k2
doAssert cast[Goo](k2) == g2
doAssert cast[Goo](k2.ord) == g2
doAssert b3.ord == 1
doAssert cast[Koo](b3) == k2
doAssert cast[Boo](k2) == b3
doAssert c3.ord == 1
doAssert cast[Koo](c3) == k2
doAssert cast[Coo](k2) == c3
static: main()
main()

View file

@ -1,5 +1,13 @@
discard """
nimout:'''
tconv.nim(81, 15) Warning: enum to enum conversion is now deprecated [User]
'''
"""
template reject(x) = template reject(x) =
static: assert(not compiles(x)) static: doAssert(not compiles(x))
template accept(x) =
static: doAssert(compiles(x))
reject: reject:
const x = int8(300) const x = int8(300)
@ -55,3 +63,26 @@ block: # issue 3766
proc r(x: static[R]) = proc r(x: static[R]) =
echo x echo x
r 3.R r 3.R
block: # https://github.com/nim-lang/RFCs/issues/294
type Koo = enum k1, k2
type Goo = enum g1, g2
accept: Koo(k2)
accept: k2.Koo
accept: k2.int.Goo
reject: Goo(k2)
reject: k2.Goo
reject: k2.string
{.define(nimLegacyConvEnumEnum).}
discard Goo(k2)
accept: Goo(k2)
accept: k2.Goo
reject: k2.string
{.undef(nimLegacyConvEnumEnum).}
reject: Goo(k2)
reject: k2.Goo