Skip to content

Conversation

@irevoire
Copy link
Contributor

@irevoire irevoire commented Oct 22, 2025

Superseed #538

Target of this PR:

  • Make as few changes as possible to the original codebase
  • Make the smallest possible LSP that gives all the information required for later
  • Don't optimize, we don't need it

What works

  • Emitting diagnostics for all kinds of errors
  • We're not interpreting the code, which means runtime errors are not displayed and infinite loops can't break the lsp
  • We have access to the list of statements, which gives us a base to implement all kinds of commands later on
  • The prelude is loaded in a temporary directory on startup so we can refer to it during execution

Unsafe use

I defined a "File" like that:

#[derive(Debug)]
pub struct File {
    // Location of the file on disk
    pub uri: Uri,
    // Location of the module in code
    pub module: ModulePath,
    // Code source id
    pub code_source_id: usize,

    // The statements reference the content which means:
    // 1. The content cannot be moved
    // 2. The statements must be dropped BEFORE the content
    pub content: Pin<String>,
    pub ast_original_stmts: Vec<ast::Statement<'static>>,
    pub ast_transformed_stmts: Vec<ast::Statement<'static>>,
    pub typed_stmts: Vec<numbat::Statement<'static>>,
    pub diags: Vec<Diagnostic>,

    // Cached info
    pub transformer: numbat::Transformer,
    pub type_checker: numbat::typechecker::TypeChecker,
}

I need to store the statements for code action, and as you can see, they have a lifetime of 'static.
In numbat-cli, that's not a big deal, as we can Box::leak the file. But in the lsp where each file modification generates an entirely new file, that's not possible.
Since the statements depend on the content, which is stored in the same struct, that also means we're doing a self-referential thingy.
I solved the issue by:

  • Pinning the content so we're sure it never moves, and the ref stays valid in the statements
  • When dropping the file, we MUST drop the statements FIRST, otherwise we'll hold reference to freed data, which is UB
  • When creating the original_stmts we have to "cast" their slice to 'static with an unsafe mem::transmute

What needs to be fixed

  • When we merge the different we have on use stmts we sometimes have to emit errors, and sometimes we must override the current env
    • When importing a module creates a dimension clash, we must return an error
  • When typechecker creates new anonymous types, they collide

@irevoire irevoire force-pushed the lsp3 branch 2 times, most recently from 5b2fac0 to 3bccf98 Compare October 22, 2025 21:41
@irevoire irevoire force-pushed the lsp3 branch 2 times, most recently from a106a13 to fad7235 Compare October 25, 2025 14:51
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.

1 participant