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

Change the runtime behavior of then #1304

Merged
merged 10 commits into from
Dec 13, 2023
Merged

Change the runtime behavior of then #1304

merged 10 commits into from
Dec 13, 2023

Conversation

konnov
Copy link
Contributor

@konnov konnov commented Dec 12, 2023

Closes #594. Similar to the behavior described in #594, @josef-widder and I have noticed that the current semantics of A.then(B) is tricky when A.then(B) evaluates to false. In this case, it is not clear whether A or B evaluated to false. This makes it hard to debug long runs. In general, engineers expect that a sequence of steps cannot just stop somewhere in the middle, which is how A.then(B) works now. This looks a bit like a deadlock, especially when we have non-deterministic choices or any.

This PR is changing the runtime semantics of A.then(B) in the simulator to the behavior that I believe is more consistent with normal programming languages. Namely, if A evaluates to false, then A.then(B) emits a runtime error saying that a computation cannot continue.

This is a breaking change. Some tests may fail. But this is good, these tests may catch problems that were not caught previously.

  • Tests added for any new code
  • Documentation added for any new functionality
  • Entries added to the respective CHANGELOG.md for any new functionality

@konnov konnov marked this pull request as ready for review December 12, 2023 13:07
Copy link
Contributor

@thpani thpani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM, I've left a few comments on the comments :)

quint/test/runtime/compile.test.ts Outdated Show resolved Hide resolved
quint/test/runtime/compile.test.ts Outdated Show resolved Hide resolved
quint/test/runtime/compile.test.ts Outdated Show resolved Hide resolved
@@ -1273,7 +1277,19 @@ export class CompilerVisitor implements IRVisitor {
// as evaluation was not successful.
this.recoverNextVars(savedValues)
this.resetTrace(just(rv.mkList(savedTrace)))
break

if (kind === 'then' && nactionsLeft > 0 && result.isJust() && !(result.value as RuntimeValue).toBool()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the way toBool is implemented, this can hide type errors if they make it through to here.

https://github.com/informalsystems/quint/blob/main/quint/src/runtime/impl/runtimeValue.ts#L523-L529

  toBool(): boolean {
    if (this instanceof RuntimeValueBool) {
      return (this as RuntimeValueBool).value
    } else {
      return false
    }
  }

That is, if the result.value is, e.g., a string, then the final conjunct will be false, and we'll end up hiding the error. This could make it really hard to catch such a problem.

To work around this, we should add a test of result.value instanceof RuntimeValueBool.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole code base of the simulator assumes that the IR is well-typed, which is ensured by the type checker. Sure, I will add an extra test, but what I should do in the case this test is failing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RuntimeValue encapsulates the types of the concrete values, so result.value instanceof RuntimeValueBool simply would not work here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could introduce a predicate just for this test, but I don't see any value in doing so. Actions and runs are always returning Booleans. This has to be enforced by the type checker.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have improved the code a bit. However, adding defensive code to this very method does seem overkill to me. For starters, it would be harder to read. On top of that, we would have to ensure type correctness in the whole compiler then, which is not the purpose of this phase.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the assumption is that everything is well typed and behavior is undefined otherwise, those conversions should be raising exceptions not returning undefined?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is a larger scale
Design problem and not limited of this pr, so I'm ok deferring this.

I would not surprised if this end up biting us tho, as we still send stuff that fails type checking to the simulator via the repl and we still have holes in the type system.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the assumption is that everything is well typed and behavior is undefined otherwise, those conversions should be raising exceptions not returning undefined?

Agree. I think the easiest thing we could do is to throw exceptions in runtime values, e.g., RuntimeValueBool could throw an exception when it is converted to something else. That would help us to defend from subtle typing errors and prevent us from introducing too much defensive code.

quint/src/runtime/impl/compilerImpl.ts Show resolved Hide resolved
quint/src/runtime/impl/compilerImpl.ts Show resolved Hide resolved
Copy link
Contributor

@shonfeder shonfeder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment along the lines I've suggested to re: the fixed id in reps. Otherwise, lgtm.

@konnov konnov enabled auto-merge (squash) December 13, 2023 10:49
@konnov konnov merged commit 42d0174 into main Dec 13, 2023
15 checks passed
@konnov konnov deleted the igor/then594 branch December 13, 2023 11:10
@shonfeder shonfeder mentioned this pull request Jan 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce ok() as a counterpart of fail()
3 participants