Accept only optional and reference types as RHS of `=?`

The fact that `option(x)` works for non-reference types (being an alias
for `some`) is an stdlib's design mistake that bites us here.
This commit is contained in:
Nickolay Bukreyev 2023-11-13 20:43:59 +07:00 committed by markspanbroek
parent c2a08bd703
commit 440debc7c3
2 changed files with 56 additions and 4 deletions

View File

@ -2,15 +2,20 @@ import std/options
import std/macros
import ./private/binderror
proc option[T](option: Option[T]): Option[T] =
template toOption[T](option: Option[T]): Option[T] =
option
template toOption[T: ref | ptr | pointer | proc](value: T): Option[T] =
# `std/options` don't consider closure iterators to be pointer types
# (probably a bug) so we don't list them here.
value.option
proc placeholder(T: type): T =
discard
template bindLet(name, expression): untyped =
let evaluated = expression
let option = evaluated.option
let option = evaluated.toOption
type T = typeof(option.unsafeGet())
let name {.used.} = if option.isSome:
option.unsafeGet()
@ -21,7 +26,7 @@ template bindLet(name, expression): untyped =
template bindVar(name, expression): untyped =
let evaluated = expression
let option = evaluated.option
let option = evaluated.toOption
type T = typeof(option.unsafeGet())
var name {.used.} = if option.isSome:
option.unsafeGet()
@ -55,7 +60,7 @@ macro bindTuple(names, expression): bool =
quote do:
let `evaluated` = `expression`
let `opt` = `evaluated`.option
let `opt` = `evaluated`.toOption
type `T` = typeof(`opt`.unsafeGet())
`letsection`
`opt`.isSome

View File

@ -165,6 +165,53 @@ suite "optionals":
else:
fail()
test "=? works with reference types":
var x = new int
x[] = 42
if a =? x:
check a[] == 42
else:
fail
x = nil
if a =? x:
fail
var p = proc = discard
if a =? p:
a()
else:
fail
p = nil
if a =? p:
fail
test "=? rejects non-reference types":
check `not` compiles do:
if a =? 0:
discard
check `not` compiles do:
if var a =? 0:
discard
check `not` compiles do:
if (a,) =? (0,):
discard
test "=? works with custom optional types":
type MyOption = distinct int
proc isSome(x: MyOption): bool = x.int >= 0
proc unsafeGet(x: MyOption): int = x.int
template toOption(x: MyOption): MyOption = x
if a =? MyOption 42:
check a == 42
else:
fail
if a =? MyOption -1:
fail
test "=? binds and unpacks tuples":
if (a, b) =? (some ("test", 1)):
check a == "test"