Skip to content

Make value capture of optionals more robust.#277

Merged
grynspan merged 1 commit intomainfrom
jgrynspan/optional-checking-more-robust
Mar 11, 2024
Merged

Make value capture of optionals more robust.#277
grynspan merged 1 commit intomainfrom
jgrynspan/optional-checking-more-robust

Conversation

@grynspan
Copy link
Contributor

@grynspan grynspan commented Mar 11, 2024

When an expression is optional and is passed through #require(), and that requirement fails, we capture the expression's subexpressions and record them as an expectation-failed event that is eventually propagated to stderr. Yay.

The underlying value-capturing machinery accepts optional values as input so that they can be lazily evaluated: if a value is nil at this layer, that means that it wasn't evaluated and we present "<not evaluated>" instead of a description of it or "nil".

In the case of #require() unwrapping optionals, these values need to be boxed in a second optional (i.e. T?? instead of T?, or Optional<Optional<T>> instead of Optional<T>) so that if the value is nil, it isn't confused with "no value". See the following table:

Expression Value of x as String?? Presented As
try #require(x) .some(.some("Hello World")) "Hello World"
try #require(x) .some(.none) nil
try #require("Goodbye Venus" ?? x) .none "<not evaluated>"

(This maybe suggests we should use a different enum than Optional here, but that's a debate for another PR I think…)

This PR fixes a couple of spots where we weren't casting our values, known to be optionals, to double-optionals, resulting in incorrect output that incorrectly included "<not evaluated>".

Checklist:

  • Code and documentation should follow the style of the Style Guide.
  • If public symbols are renamed or modified, DocC references should be updated.

When an expression is optional and is passed through `#require()`, and that
requirement fails, we capture the expression's subexpressions and record them as
an expectation-failed event that is eventually propagated to `stderr`. Yay.

The underlying value-capturing machinery accepts optional values as input so
that they can be lazily evaluated: if a value is `nil` at this layer, that means
that it wasn't evaluated and we present `"<not evaluated>"` instead of a
description of it or `"nil"`.

In the case of `#require()` unwrapping optionals, these values need to be boxed
in a second optional (i.e. `T??` instead of `T?`, or `Optional<Optional<T>>`
instead of `Optional<T>`) so that if the value is `nil`, it isn't confused with
"no value". See the following table:

| Expression | Value of `x: String?` | Presented As |
|-|-|-|
| `try #require(x)` | `.some("Hello World")` | `"Hello World"` |
| `try #require(x)` | `.some(.none)` | `nil` |
| `try #require("Goodbye Venus" ?? x)` | `.none` | `"<not evaluated>"` |

(This maybe suggests we should use a different enum than `Optional` here, but
that's a debate for another PR I think…)

This PR fixes a couple of spots where we weren't casting our values, known to be
optionals, to double-optionals, resulting in incorrect output that incorrectly
included `"<not evaluated>"`.
@grynspan
Copy link
Contributor Author

@swift-ci please test

@grynspan grynspan self-assigned this Mar 11, 2024
@grynspan grynspan added the bug 🪲 Something isn't working label Mar 11, 2024
@grynspan grynspan merged commit c0e4f59 into main Mar 11, 2024
@grynspan grynspan deleted the jgrynspan/optional-checking-more-robust branch March 11, 2024 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug 🪲 Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants