No rls and rustc integration?


#1

Recently I was pointed out to this rls issue.

Particularly this @nrc comment:

We basically can’t share data between RLS and non-RLS because the RLS builds are done with a custom compiler and with different flags. Even if we did try to unify the two, it could mean that a user could cause there CLI use of Cargo to rebuild due to the RLS needing to rebuild or vice versa (and also there mightbe changes that the RLS doesn’t know about so doesn’t rebuild when it should)

And as I understand this long-term solution. And I wonder why? cargo check and cargo build perfectly coexist with each other, why rls not use results of cargo check?

For example there is ongoing work on clang based LSP server and it would/should integrate feature Index‐While‐Building, so compiler and IDE can share the same data, and do not duplicate efforts. This is important for C++, because of compilation and IDE’s indexing too slow.

But compilation of rust code now is slower than c++, why not reuse results? I mean not not only results of running build.rs as suggested in rls issue referred above , but also share cargo check and cargo build results?


#2

I thought about this the other day and think that we could explore trying to reuse the data, but it’s not as trivial as one would think.

So in order for RLS to work on stable, it acts as the compiler itself - it spawns itself as rustc shim with all the flags that rustc normally accepts (it sets its unstable flags programatically, when acting as the compiler driver).

Due to this, the environment in which the RLS-compiler is executed differs slightly from the one that rustc is normally executed in, e.g. when calling cargo check. At best the compiler will just emit different metadata hash but still emit compatible build artifacts (think different env vars or args that do not change the resulting artifacts), at worst the resulting build data will be completely incompatible.

Then again, I don’t think it’s not worth exploring the, at least partial, solution space for the problem. For example I think we could see if it’s possible to enforce cross RLS-rustc reusable build artifacts for external dependencies, assuming that users will opt-in into that and is aware that possibly meddling with regular cargo builds while RLS is working may provide erroneous results. Or summon a dragon, you never know. :smile:


#3

But why it should works with stable as external rustc process? At now it is part of rust distribution, and installed via rustup component add --toolchain nightly rls-preview, I thought stage where it works with rustc as with black box was long time ago, and now it shares code with rustc?

But why? If cargo check doesn’t provide enough information or have different restrictions (like doesn’t work with not saved yet file), why not solve it on cargo check level - new options, json input and output and so on. But extend cargo check in so way that cargo check --rls-options will provide compatible data with cargo check, more data, but in the same format.

So running cargo check from IDE that uses rls would be just as lightning?

As I understand this is modern C++ IDE works, they have it’s own index, but also can read precompiled headers produced by compiler, and at now because of clang’s infrastructure integrate building index for IDE into compiler.

Why not do the same with rustc/rls from start? As I now they building and deploying into rustup artifact repo as the whole?


#4

The main motivation for running the compiler in-process (which means linking to compiler and using its internals) is to reduce the overhead of passing and marshalling data around. This allows to speed up builds and we also pass reuse current file buffers in-memory iirc, which also simplifies things.

Using compiler internals like that is why we probably won’t just split from compiler, especially since using compiler-backed code completion is in the works.

Currently it’d be easy to build a cargo watch-like tool, which compiles with -Zsave-analysis (nightly only, emitting save analysis files per each crate target). This could be loaded on every change by the rls-analysis index to be later reused by this different RLS. However I think it’d prove to more hassle than it’s worth, since:

  1. -Zsave-analysis would need to be stabilised, which is very unlikely, since it’s essentially emitting compiler internals and how it represents data to the world)
  2. We’d have to guarantee that the emitted data can be properly understood by the used rls-analysis (we’d have to tie those versions anyway or enforce backwards-compatibility - but this is internally-represented data by the compiler)
  3. Throwing and (de)serializing data around so often would be a lot slower and inefficient - imagine changing one letter and re-checking a big crate (which means we need to reread big save-analysis json file again), only to realize that the index didn’t effectively change.

And so forcefully splitting RLS off the compiler seems to do more harm than good here.

I answered the bit about data marshalling overhead, but I do think it might be possible to reuse data for externally compiled crates. I didn’t investigate how hard would it be and if we can even do it right now, but I think we’d need to be careful about matching data formats and whatnot.