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

Evaluating commands fails after #168 #171

Merged
merged 6 commits into from
Dec 5, 2023

Conversation

9999years
Copy link
Member

@9999years 9999years commented Dec 1, 2023

#168 introduced another bug with eval commands. The actual bugfix here is relatively small, but I needed some architectural changes in the test harness (to support restarting the ghciwatch session) and in the ghciwatch codebase to make these changes.

  • Removes the system of Modes introduced in Run tests on save #11 and gutted in Make --errors output compatible with ghcid #78. This grouped ghci output into buffers based on the "mode" ghciwatch was in when the output was produced (modules being compiled, tests being run, internal data being gathered, etc.). Over time this was used less and less. Now we parse diagnostics out of all compiler output and it's fine.
  • Also removes the Ghci::process_ghc_messages method introduced in Fix "module defined in multiple files" by parsing GHC diagnostics #77. The idea was that methods like Stdout::prompt would return a list of parsed GHC messages/diagnostics, and those would be processed by Ghci. In practice, it was difficult to remember to pass the lists of messages to process_ghc_messages consistently. Now, each method that produces diagnostics takes a &mut CompilationLog. There's many places where messages are produced, but only a few where messages need to be written out (after reloads, after startup), so this is a lot more convenient.

Bug reproduction details

There's two bugs here: module '...' is not interpreted and module '...' defined in multiple files. My attempted fix for the first bug in #168 caused the second bug. This PR fixes both.

First, you load a ghci session:

$ ghci
[1 of 4] Compiling MyLib            ( src/MyLib.hs, interpreted )
[2 of 4] Compiling MyModule         ( src/MyModule.hs, interpreted )
[3 of 4] Compiling Paths_my_simple_package ( /Users/wiggles/ghciwatch/tests/data/simple/dist-newstyle/build/aarch64-osx/ghc-9.6.3/my-simple-package-0.1.0.0/l/test-dev/build/test-dev/autogen/Paths_my_simple_package.hs, interpreted )
[4 of 4] Compiling TestMain         ( test/TestMain.hs, interpreted )
Ok, four modules loaded.
ghci> :quit

This interprets each module. When the GHC option -fwrite-if-simplified-core (introduced in GHC 9.4) is used, loading the ghci session additionally writes interface files for each module:

The interface file will contain all the bindings for a module. From this interface file we can restart code generation to produce byte-code.

Then, you can load a new ghci session, which will load the interface modules and use them to generate ghci byte-code directly. Critically, this means that the modules are not considered to be "interpreted", leading to this error message:

$ ghci
Ok, four modules loaded.
ghci> :module + *MyLib
module 'MyLib' is not interpreted; try ':add *MyLib' first

How ghciwatch triggers the bug

When ghciwatch's --enable-eval flag is used, an eval command runMyCommand for a module MyModule is executed roughly like this:

> :module + *MyModule
> runMyCommand
> :module - *MyModule

:module + *MyModule brings the top-level definitions of MyModule into scope, so that eval commands can refer to definitions in the module they're defined in.

@parsonsmatt noticed that if ghci loaded a module from one of these interface files, evaluating commands in that file would fail:

GHCi, version 9.6.2: https://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from .ghci
• ghci started in 5.52s

• Adding modules to ghci:
  • test/Foo.hs

Ok, 3140 modules loaded.
• test/Foo.hs:25:7: runMyTests
module 'Foo' is not interpreted; try ':add *Foo' first

I initially attempted to fix this in #168, which added an explicit :add *Foo before running the eval command, which forces the module to be interpreted.

Unfortunately, using the module name instead of the module path could lead to the module being added twice, causing the dreaded module defined in multiple files bug (see #37, #77, #125).

Copy link

linear bot commented Dec 1, 2023

@github-actions github-actions bot added the patch Bug fixes or non-functional changes label Dec 1, 2023
@9999years 9999years force-pushed the rebeccat/dux-1650-eval-fails-after-168 branch from 45ec922 to 2a439ac Compare December 1, 2023 20:54
@9999years 9999years force-pushed the rebeccat/dux-1650-eval-fails-after-168 branch from c5bdf99 to 83107eb Compare December 4, 2023 21:10
@9999years 9999years marked this pull request as ready for review December 4, 2023 21:21
// We use `:add *{module}` to force interpreting the module. We do this here instead of in
// `add_module` to save time if eval commands aren't used (or aren't needed for a
// particular module).
self.interpret_module(&path, log).await?;
Copy link
Member Author

Choose a reason for hiding this comment

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

Here's the actual bugfix, making sure the modules are interpreted and using the module path (instead of the module name) to import them.

@9999years 9999years requested a review from parsonsmatt December 5, 2023 00:36
@9999years 9999years merged commit c42da6c into main Dec 5, 2023
28 checks passed
@9999years 9999years deleted the rebeccat/dux-1650-eval-fails-after-168 branch December 5, 2023 19:05
Copy link
Contributor

github-actions bot commented Dec 5, 2023

9999years added a commit that referenced this pull request Apr 15, 2024
Our favorite bug is back! We have a situation where `ghciwatch` loads
all the modules at the start of the session, so they're all loaded:

```
ghci> :show targets
Foo
Bar
Baz
```

Then, an eval command in (for example) module `Foo` is executed. To
start with, the module is added and explicitly interpreted:

```
ghci> :add *src/Foo.hs
```

Unfortunately, this always uses the module path (see
[#171](#171)), so
if `ghciwatch` wasn't the one to load the module (e.g. it was loaded at
the start of the session), the module path and module name will conflict
and lead tot he dreaded "module is defined in multiple files".

See:
[https://gitlab.haskell.org/ghc/ghc/-/issues/13254#note_525037](https://gitlab.haskell.org/ghc/ghc/-/issues/13254#note_525037)

# Solution

I think we can fix this by keeping track of how each module is added to
the session — as a path or as a module name — and then only using that
form going forward.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
patch Bug fixes or non-functional changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants