Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make Option[T] sane again for pointer types #18401

Closed
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
- `addr` is now available for all addressable locations, `unsafeAddr` is deprecated and
becomes an alias for `addr`.

- `std/options` now doesn't special case pointers, and `some(nil).isSome` is now true.

- io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`.


## Standard library additions and changes

- `macros.parseExpr` and `macros.parseStmt` now accept an optional
Expand Down
62 changes: 27 additions & 35 deletions lib/pure/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -77,25 +77,28 @@ when defined(nimPreviewSlimSystem):

when (NimMajor, NimMinor) >= (1, 1):
type
SomePointer = ref | ptr | pointer | proc
SomePointer = ref | ptr | pointer | proc # xxx what about cstring? proc vs closure?
else:
type
SomePointer = ref | ptr | pointer

type
Option*[T] = object
## An optional type that may or may not contain a value of type `T`.
## When `T` is a a pointer type (`ptr`, `pointer`, `ref` or `proc`),
## `none(T)` is represented as `nil`.
when T is SomePointer:
val: T
else:
val: T
has: bool
val: T
has: bool

UnpackDefect* = object of Defect
UnpackError* {.deprecated: "See corresponding Defect".} = UnpackDefect

template maybeOption*[T](_: typedesc[T]): untyped =
## Return `T` if T is `ref | ptr | pointer | proc`, else `Option[T]`
runnableExamples:
assert maybeOption(ref int) is ref int
assert maybeOption(int) is Option[int]
when T is SomePointer: T
else: Option[T]

proc option*[T](val: sink T): Option[T] {.inline.} =
## Can be used to convert a pointer type (`ptr`, `pointer`, `ref` or `proc`) to an option type.
## It converts `nil` to `none(T)`. When `T` is no pointer type, this is equivalent to `some(val)`.
Expand All @@ -106,14 +109,15 @@ proc option*[T](val: sink T): Option[T] {.inline.} =
runnableExamples:
type
Foo = ref object
a: int
b: string

assert option[Foo](nil).isNone
assert option(Foo(nil)).isNone
assert some(Foo(nil)).isSome
assert option(42).isSome

result.val = val
when T isnot SomePointer:
when T is SomePointer:
result.val = val
result.has = val != nil
else:
result.val = val
result.has = true

proc some*[T](val: sink T): Option[T] {.inline.} =
Expand All @@ -125,16 +129,15 @@ proc some*[T](val: sink T): Option[T] {.inline.} =
## * `isSome proc <#isSome,Option[T]>`_
runnableExamples:
let a = some("abc")

assert a.isSome
assert a.get == "abc"

when T is SomePointer:
assert not val.isNil
result.val = val
else:
result.has = true
result.val = val
let b: ref int = nil
assert some(b).isSome
assert option(b).isNone

result.has = true
result.val = val

proc none*(T: typedesc): Option[T] {.inline.} =
## Returns an `Option` for this type that has no value.
Expand Down Expand Up @@ -162,11 +165,7 @@ proc isSome*[T](self: Option[T]): bool {.inline.} =
runnableExamples:
assert some(42).isSome
assert not none(string).isSome

when T is SomePointer:
not self.val.isNil
else:
self.has
self.has

proc isNone*[T](self: Option[T]): bool {.inline.} =
## Checks if an `Option` is empty.
Expand All @@ -177,11 +176,7 @@ proc isNone*[T](self: Option[T]): bool {.inline.} =
runnableExamples:
assert not some(42).isNone
assert none(string).isNone

when T is SomePointer:
self.val.isNil
else:
not self.has
not self.has

proc get*[T](self: Option[T]): lent T {.inline.} =
## Returns the content of an `Option`. If it has no value,
Expand Down Expand Up @@ -342,10 +337,7 @@ proc `==`*[T](a, b: Option[T]): bool {.inline.} =
assert b == d
assert not (a == b)

when T is SomePointer:
a.val == b.val
else:
(a.isSome and b.isSome and a.val == b.val) or (a.isNone and b.isNone)
(a.isSome and b.isSome and a.val == b.val) or (a.isNone and b.isNone)

proc `$`*[T](self: Option[T]): string =
## Get the string representation of the `Option`.
Expand Down
2 changes: 1 addition & 1 deletion testament/important_packages.nim
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter"
pkg "norm", "testament r tests/sqlite/trows.nim"
pkg "npeg", "nimble testarc"
pkg "numericalnim", "nimble nimCI"
pkg "optionsutils"
pkg "optionsutils", allowFailure = true # pending https://github.com/PMunch/nim-optionsutils/pull/6
ringabout marked this conversation as resolved.
Show resolved Hide resolved
pkg "ormin", "nim c -o:orminn ormin.nim"
pkg "parsetoml"
pkg "patty"
Expand Down
2 changes: 1 addition & 1 deletion tests/stdlib/toptions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ proc main() =
doAssert(option(intref).isSome)

let tmp = option(intref)
doAssert(sizeof(tmp) == sizeof(ptr int))
doAssert(sizeof(tmp) > sizeof(ptr int))

var prc = proc (x: int): int = x + 1
doAssert(option(prc).isSome)
Expand Down