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

Oddities of top-level binds #2162

Open
sauclovian-g opened this issue Dec 17, 2024 · 1 comment
Open

Oddities of top-level binds #2162

sauclovian-g opened this issue Dec 17, 2024 · 1 comment
Assignees
Labels
tech debt Issues that document or involve technical debt type: bug Issues reporting bugs or unexpected/unwanted behavior usability An issue that impedes efficient understanding and use
Milestone

Comments

@sauclovian-g
Copy link
Contributor

sauclovian-g commented Dec 17, 2024

Suppose I start out with the following definitions:

let f x = return x;
let g x = do { disable_crucible_profiling; return x; };
let h x = do { llvm_assert {{ True }}; return x; };

These give me the following types:

sawscript> :t f
{a.0, a.1} a.0 -> a.1 a.0
sawscript> :t g
{a.0} a.0 -> TopLevel a.0
sawscript> :t h
{a.0} a.0 -> LLVMSetup a.0

which is all as expected.

I can then run g 3 at the top level:

sawscript> g 3
[02:44:05.115] 3

I cannot run h 3 at the top level because it's in the wrong monad. It doesn't fail, though, just passes the value through:

sawscript> h 3
[02:45:16.771] <<monadic>>
sawscript> :t h 3
LLVMSetup Int

If I try to run f 3 it fails:

sawscript> f 3

Stack trace:
Not a monomorphic type: {a.3} a.3 Int

That is, it refuses to run a value that's polymorphic over its monad in the top level monad. This is consistent with it not failing on h 3, in the sense that it's evidently not trying to unify the type with TopLevel a. However, it doesn't seem like desirable behavior.

This behavior is also special-cased in the (syntactic) top level. In nested do-blocks the monad type is unified across the do-block:

sawscript> let foo () = do { a <- f 3; b <- g 3; return (a, b); };
sawscript> :t foo
() -> TopLevel (Int, Int)
sawscript> foo ()
[02:57:21.549] (3,3)

and

sawscript> let bar () = do { a <- f 3; b <- h 3; return (a, b); };
sawscript> :t bar
() -> LLVMSetup (Int, Int)
sawscript> bar ()
[02:58:35.553] <<monadic>>

and if you don't pin it down, it stays general:

sawscript> let baz () = do { a <- f 3; return a; };
sawscript> :t baz
{a.0} () -> a.0 Int

I think this is also undesirable and we'd be better off if the syntactic top level were equivalent to a do-block in the TopLevel monad.

(Since this weirdness is a result of the interpreter special-casing its top level, I expect it also applies to a ProofScript repl, but I haven't checked that.)

(Also, I'm not convinced this prohibition against evaluating a polymorphic term at the top level serves any purpose. But perhaps that's a separate issue.)

Note though that changing this might cause existing badly-written scripts to fail if they do <- at the top level in places where they ought to have done let.

@sauclovian-g sauclovian-g added type: bug Issues reporting bugs or unexpected/unwanted behavior usability An issue that impedes efficient understanding and use tech debt Issues that document or involve technical debt labels Dec 17, 2024
@sauclovian-g sauclovian-g added this to the 2024T3 milestone Dec 17, 2024
@sauclovian-g sauclovian-g self-assigned this Dec 17, 2024
sauclovian-g added a commit that referenced this issue Dec 18, 2024
…binds.

This corrects certain weirdnesses that were probably intended as
ease-of-use affordances for the repl but also affect scripts. In
particular:
   - monadic actions need to be in TopLevel (or in ProofScript for
     the ProofScript repl);
   - monadic actions that are polymorphic in their monad are run
     in TopLevel or ProofScript instead of being gratuitiously
     rejected;
   - the interpreter no longer wraps pieces of the bind in a
     Decl for typechecking, which is the first step on cleaning
     up the interpreter's typechecking notions (#2158).

The downside is that you can no longer directly eval non-monadic
expressions in the repl without prefixing them with "return". This is
not terrible, but we should at least consider adding repl-specific
affordances to allow that again once the code is in shape to be able
to do it reasonably.

Another oddity is that previously if you eval'd a monadic action that
was in some other monad (e.g. LLVMSetup) at the syntactic top level it
would, instead of giving a type error, just not evaluate it and then
print "<monadic>" as the result. If for some reason you really want
to do this you can also get the old behavior by prefixing the lot with
"return".

Note that all this applied to the _syntactic_ top level, and not binds
within do-blocks, whether or not they were within functions, and
regardless of whether they were in the TopLevel monad.

Include some tests and a CHANGES entry. Closes #2162.
sauclovian-g added a commit that referenced this issue Dec 20, 2024
Statements of the forms

   x <- e;
   x <- s;
   s;

where e is a non-monadic term and s is a monadic term in the wrong
monad are now accepted with a warning instead of causing a fatal
type error.

Note that statements of the form

   e;

which also used to be accepted will now fail. The chance of this
appearing in the wild is low.

While I've fixed all the fallout in the saw tree, the amount (and
nature) suggests that downstream users will also have cases to fix and
we should allow time for this before making it a hard fail.

Add tests for all these forms.
@sauclovian-g
Copy link
Contributor Author

It turns out that quite a few of our own scripts do in fact use <- at the top level in places where they should have used let. Also, a couple cases have turned up where an action in the wrong monad entirely was used at the top level, which previously was silently ignored.

Because of the likelihood of further fallout downstream among users, I've added workarounds so that the following forms generate warnings:

  • x <- e; for non-monadic e
  • x <- e; for monadic e in the wrong monad
  • e; for monadic e in the wrong monad

These warnings will be kept as warnings for at least one release and possibly longer, but will eventually be changed to errors.

Note that the following form which was previously accepted (and ignored because it obviously does nothing) is now rejected:

  • e; for non-monadic e

All of the above applies only at the top syntactic level. Within explicit do-blocks all of these forms were previously, and still are, rejected.

I've opened a separate ticket (#2167) for converting these warnings to errors in the future so it doesn't get forgotten.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tech debt Issues that document or involve technical debt type: bug Issues reporting bugs or unexpected/unwanted behavior usability An issue that impedes efficient understanding and use
Projects
None yet
Development

No branches or pull requests

1 participant