Skip to content

Conversation

@sauclovian-g
Copy link
Contributor

This has been a target for months. But it's not the end; now that it's possible to reason about what the interpreter's actually doing with its environments, it becomes possible to fix the interaction between the interpreter and the typechecker, which is also accursed. No rest for the wicked, or something.

Closes #1646, and possibly others, as the bizarre implementation has given rise to all manner of weirdness.

@sauclovian-g
Copy link
Contributor Author

force-push was: left something off at the last minute before I pushed. :-|

x=a
test1.saw:8:5-8:6: Warning: Redeclaration of x
test1.saw:3:5-3:6: Warning: Previous declaration was here
x=b
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 anyone's wondering whether git's inability to track renames reliably is real, this right here is evidence. I renamed both the .saw and .good files...

@sauclovian-g
Copy link
Contributor Author

There's also something horrible going on with the Cryptol environment that's broken a bunch of tests.

I think what's happening is:

  • the old code added to the Cryptol environment at extendEnv time; the new code does as well, but without the LocalEnv stuff this happens sooner and more aggressively now;
  • the Cryptol environment doesn't scope, which is a known issue;
  • there's therefore more opportunities for a nested function to add stuff to the Cryptol environment that blitzes something already there and causes problems in the caller.

At least the first failure in test0043_invariant_3 seems to have this pattern: one function binds a local n, then it calls a function with an argument n, and the argument overwrites the Cryptol binding for the original n and later references get the wrong thing and fail.

With the various woolly ways the LocalEnv was getting manipulated it's quite likely that there have always been ways to step on this rake and it's just easier now.

I don't want to try fixing the scoping of the Cryptol environment in this pull request, because it's not trivial and may need design discussions to do correctly, so I think I'm going to hack up something ugly as a stopgap.

@sauclovian-g sauclovian-g force-pushed the interpreter-environments branch from d2c73d8 to 9c83ad5 Compare October 17, 2025 01:43
@sauclovian-g
Copy link
Contributor Author

This push fixes the oversights, the miscellaneous suggestions above, and the ScopedMap haddocks (I actually built the haddocks for a change and they render ok now) ... but not the Cryptol environment problem yet.

@sauclovian-g
Copy link
Contributor Author

So, it's even more broken that way. It'll proabably be Monday or Tuesday evening before I can get back to this...

@sauclovian-g
Copy link
Contributor Author

force-push: seems it needed to be rebased over or merged with #2593 for the CI to run. This also crossed some other recent merges.

If the current batch of changes works (or, likely, even if it doesn't) I'm going to retcon the whole patchset anyway as it needs some attention to naming and some factoring out of common logic in saw-server and in the REPL code.

@sauclovian-g
Copy link
Contributor Author

Foo.

[21:19:52.086] Attempted to register the following name twice: cryptol:thm#5921

FAIL (1.96s)
    intTests/IntegrationTest.hs:185:
    expected: ExitSuccess
     but got: ExitFailure 2
    Use -p '/test_ecdsa/' to rerun this test only.

(that's the behavior when the lack of SAWCore scoping bites)

I'm inclined to rejigger that test to avoid the problem, instead of committing to hack around it right now. Any objections?

@RyanGlScott
Copy link
Contributor

I assume that this happens because there are multiple inner scopes that each define something local named thm? If so, then I would be fine with working around it, perhaps by renaming the thms to avoid "conflicts".

@sauclovian-g
Copy link
Contributor Author

Yes; it turns out there's twelve of them, of the form let {{ thm x = ... }}; let t = unfold_term ["thm"] {{ thm }}; whereas other things in the file just do let t = {{ \x -> ... }}. I am going to try changing them all to the latter format, since that ought to work perfectly well.

Copy link
Contributor

@RyanGlScott RyanGlScott left a comment

Choose a reason for hiding this comment

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

It took quite a bit of huffing and puffing, but it looks like you've finally patched things up. I'm content to land this and make further improvements later.

@sauclovian-g
Copy link
Contributor Author

Ok, now it at least works. Next step is to whip it into committable shape...

@sauclovian-g
Copy link
Contributor Author

(that is: can definitely make further improvements later, but the last few commits were hacks and ideally the commits that eventually get merged should all build and pass tests)

@sauclovian-g sauclovian-g force-pushed the interpreter-environments branch from 3bd79bb to d264998 Compare October 21, 2025 18:22
@sauclovian-g
Copy link
Contributor Author

This force-push rearranges commits and improves commit messages, with no net resultant difference.

@sauclovian-g sauclovian-g force-pushed the interpreter-environments branch from d264998 to aef27b5 Compare October 22, 2025 03:36
@sauclovian-g
Copy link
Contributor Author

This force-push tidies up the Cryptol environment changes and moves them to before the main environment cleanup commit, which allows them to be their own commit without incurring a broken intermediate state.

Changes from the previous version (since there's no way to put this in the commit messages):

Naming:

  • Use CryptolEnvStack instead of CryptolScopeStack as the type name for the stack of environments; then we have CryptolEnv and CryptolEnvStack.
  • The canonical variable name for a CryptolEnv will be ce or cenv.
  • The canonical name for a CryptolEnvStack will be cryenvs.

Accessors:

  • Because saw-erver and the REPL both use TopLevelRW from a monad context that isn't TopLevel, provide an additional set of Cryptol environment accessors that work on an explicit TopLevelRW. Use these to avoid exposing exactly how the Cryptol environment appears in TopLevelRW.

Simplify:

  • Use CryptolEnvStack CryptolEnv [CryptolEnv] instead of an explicit NonEmpty; we don't use any NonEmpty ops on the NonEmpty and this avoids the need to import the :| operator all over the place.
  • Use the new accessors.
  • Tidy some of the code.

- The members of the Refs record should have the prefix "r", not "e".
- Also they should all have a prefix, not some of them.
- Don't call the TopLevelRW the "environment"; that is already plenty
  confusing and will be growing more so shortly.

NFCI
This is like Data.Map except it's a chain of maps that function as
scopes; keys in inner (more recent) scopes override matching keys in
outer (older) scopes, as one wants for scoped variable lookup.

You can also push and pop the scope entries as you carry the map in
and out of scoping constructs.

Also add SAWSupport.Panic because there isn't one yet and ScopedMap
has a panic case: trying to pop the last scope.
These mysteriously behave differently; undefined9 crashes and
undefined8 doesn't, even though they're almost identical and _should_,
one would _think_, have the same eval behavior up to the point where
"undefined" appears. Like undefined7, this is probably a function of
the strange way the SAWScript interpreter handles its variable
environments.

I've been sitting on these for a while; not sure why I didn't merge
them before.
There's about to be more than ten tests in here; insert zeros
so things run in the expected order.

For git safety this commit contains only renames; since the
filenames appear in the reference outputs, they'll be updated
next.
The reference outputs here reflect the current behavior, not the
desired behavior.
This removes uses of "let {{ thm = ... }}" followed by explicitly
binding and unfolding "thm" with just binding a Cryptol-level lambda,
like other code in the same file. This is simpler and probably better
for an example regardless of other considerations.

But also, the "let {{ thm = ... }}" binds a SAWCore-level name "thm",
which causes errors about redefining it because the SAWCore
environment doesn't get SAWScript-level scopes. That's #2726.

After the upcoming fixes so the Cryptol environment _does_ get
SAWScript-level scope handling it does, anyway. It isn't obvious why
this code didn't already exhibit that behavior. Perhaps there's
something down inside that overwrites the corresponding SAWCore
definition when you overwrite a Cryptol definition, which no longer
applies since we no longer do that. (And maybe that logic checks to
make sure the SAWCore manipulation is sound, and maybe it doesn't...)

Regardless, we have at least one other test that explicitly
captures #2726.
A CryptolEnvStack is a stack of CryptolEnv; the idea is that the stack
allows pushing and popping the Cryptol environment at runtime to match
SAWScript scopes.

This adds the possibility of having the Cryptol environment track the
SAWScript scopes as they come and go, in pursuit of #2304. However,
it's not hooked up yet. The interpreter environments cleanup needs to
happen before that's feasible; however, making these changes first
allows them to appear as their own commit.

(It was found that fixing the interpreter's environment handling
demanded also fixing the Cryptol environment scoping. Otherwise a lot
of things break. The interpreter's old environment handling defers
updating the Cryptol environment in some circumstances, which is
apparently enough to get a closer approximation of correct behavior in
the Cryptol environment than a rational approach. Because of this,
these changes were first written on top of the environment handling
cleanup and then moved before it. Because of _that_, this commit has
not been tested with a full CI run, because GH won't run actions on
commits that aren't the tips of pull-request branches. I've taken some
precautions, and I _think_ I've kept every access to the Cryptol
environment the same with respect to using "getMergedEnv", but I might
have fumbled one, or something else might break. Anyway, there's some
risk of this commit turning out to be non-testable in the future. It
is not _supposed_ to make any functional change.)

Note that there are a couple new error paths where attempting
import-like operations in a nested scope will fail; these are not
reachable until the scopes are connected up.

Besides that:

 - Add more accessors for the Cryptol environment and deploy them to
simplify the accesses in the REPL and in saw-server. This avoids
baking in exactly where and how the Cryptol environment appears in
TopLevelRW (note that saw-server shouldn't be using TopLevelRW at all
anyway...)

 - Canonicalize some of the related naming:
      - The canonical variable name for a CryptolEnv will be "ce" or
        "cenv", plural "ces" or "cenvs".
      - The canonical name for a CryptolEnvStack will be "cryenvs".

 - Tidy some of the adjacent code.
There are many notable consequences:

   - The unexplained weirdness that appeared with "undefined" in
     test_sawscript_builtins is now fixed. Yay! No idea why though,
     other than the old code was terrible.

   - Similarly, bindings no longer escape "proof_subshell", which
     closes #2243.

   - The old dynamic scope bug behavior (see #1646) is removed.

   - We now have, via attaching the new CryptolEnvStack code, and
     including the Cryptol environment in the new Environ type so
     things get closed in properly, reasonably correct scope handling
     for the Cryptol environment. This closes #2304.

   - Note though that the SAWCore environment properly speaking needs
     a similar treatment, but we can't currently. The result is that
     in obscure cases (e.g. test03.saw in test2304) using the same
     variable name in what should be distinct places can cause a
     SAWCore-level duplicate definition error. For the moment, we're
     going to hope that these cases are all sufficiently obscure that
     nobody trips on them, and track the issue in #2726.

Test reference (and in some cases comments) updates:
   - test1646/test01 and test02: the already-bound variable
     no longer updates; this fixes the type system hole and ensuing
     panic previously shown in test02.
   - test1646/test05 is now broken; it was relying on the old dynamic
     scope bug behavior to implement "let rebindable". This will be
     fixed in the next commit.
   - test1646/test14 now behaves differently but is still wrong. This
     will be fixed in the next commit.
   - test2304/test02 now works correctly.
   - test2304/test03 now works at the Cryptol level but triggers #2726.
   - test_sawscript_builtins/undefined7 and undefined8: these now
     crash where they're supposed to.
This makes the new "let rebindable" functionality work, fixing the
test05 and test14 cases of test1646, and finally closes #1646.
@sauclovian-g sauclovian-g force-pushed the interpreter-environments branch from aef27b5 to 190d563 Compare October 22, 2025 03:43
@sauclovian-g
Copy link
Contributor Author

This force-push rebases on master.

@sauclovian-g
Copy link
Contributor Author

This should be ready to go for real; it is probably worth at least glancing at the tidy-up force-push diff in case I did something stoopid.

@sauclovian-g
Copy link
Contributor Author

Also, this closes #2304 and #2243, in addition to #1646, but I can't get the issue-linking widget to recognize that they exist.

These are now explicitly prohibited in nested blocks.
@sauclovian-g sauclovian-g merged commit d1b1645 into master Oct 22, 2025
37 checks passed
@sauclovian-g sauclovian-g deleted the interpreter-environments branch October 22, 2025 13:39
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.

Cryptol bindings escape functions Bindings escape proof_subshell Design an alternative to the dynamic scope bug

3 participants