# This Month in @compiler-errors (rustc contributions) - October I've seen some "this month in XYZ" posts recently, and I'm interested in posting my own, highlighting a variety of things I've been contributing to in the Rust compiler in the last month. This covers the month of October, so it's a bit out of date. <details> <summary>More details</summary> Although I think I'm quite a prolific contributor to the Rust compiler, I often forget exactly what I'm doing day to day and week to week. My contributions often come in response to my being made aware of things that need urgent help, pings from people, hearing about things from the tweet-o-sphere, or most rarely, just having a strike of random inspiration. So, ironically, when I'm "between" work, I often feel like motivation is difficult to find, and feel a bit of despair that maybe I've finally run out of things to do -- impostor syndrome sucks! And it never goes away! Hopefully these blog post will help with that. </details> ### Const traits Of all the things I've done in the last month, the one that I am most proud of is reworking the internal representation of *const traits* in the compiler. Const traits were originally proposed in [RFC 2632](https://github.com/rust-lang/rfcs/pull/2632), then revamped heavily into a design that is still yet to be RFC'd ([drafted here](https://internals.rust-lang.org/t/pre-rfc-revamped-const-trait-impl-aka-rfc-2632/15192)), but still is being heavily experimented on[^spiel]. [^spiel]: I think this work has highlighted both the effectiveness and importance of pre-RFC experimentation of features in the compiler. While I applaud the original RFC for proposing what I think is one of the most important not-yet-stable features in rustc -- like for crying out loud what do you mean we can't call methods in const functions -- we would never have known what we knew today about the feature had it been RFC'd in its original state. In [#131985 <small>"Represent trait constness as a distinct predicate"</small>](https://github.com/rust-lang/rust/issues/131985), I completely reworked the way that we represent and validate `const` and `~const` trait bounds. This work came out of a discussion I had on a messaging platform: > errs: i wonder if we should actually just represent this effectness as a separate predicate.... > errs: rather than an associated const > [...] > errs: bc what we have is an only-if relationship > errs: we could stop worrying about this higher-ranked stuff if we had a specific (perhaps set of) predicate that represented the effects rather than trying to use traits and projections For those who are more interested in the technical details, I highly recommend reading the description of the PR linked above. It also includes some really detailed background about the previous few approaches to implementing const traits and const trait bounds. Due to this rework, I was able to implement support for `~const` in "item bound" position ([#132118 <small>"Add support for ~const item bounds"</small>](https://github.com/rust-lang/rust/issues/132118)), i.e., in an associated type bound like: ```rust #[const_trait] trait Foo { type Type: ~const Bar; } ``` Also, we had previously introduced the artificial limitation that const traits only worked correctly when the new trait solver (`-Znext-solver`) was enabled. In [#132119 <small>"Hack out effects support for old solver"</small>](https://github.com/rust-lang/rust/pull/132119), we were able to relax that. Since the standard library builds using the *old*, stable trait solver today, this unlocks the reintroduction of const traits in the near future, which were [removed](https://github.com/rust-lang/rust/issues/110395) when the previous `feature(effects)` rewrite of const traits began. This work also led to some nice compiler cleanups ([#132344](https://github.com/rust-lang/rust/issues/132344), [#131652](https://github.com/rust-lang/rust/issues/131652), [#131653](https://github.com/rust-lang/rust/issues/131653), [#131968](https://github.com/rust-lang/rust/issues/131968), [#132368](https://github.com/rust-lang/rust/issues/132368)). #### Side-note: bound modifiers I'm not certain if it was totally due to this `~const` rework that I did, but I did spend some time also reworking how we represented and lowered "trait bound modifiers" in the frontend of the compiler. Trait bound modifiers are the bits that "modify" a trait bound -- think `?` on `Sized`, and `const`/`~const` on const traits, along with the `async` keyword proposed to modify `Fn` in [RFC 3668 <small>"Async closures"</small>](https://rust-lang.github.io/rfcs/3668-async-closures.html). This work led to some nice compiler cleanups ([#131931](https://github.com/rust-lang/rust/issues/131931), [#131981](https://github.com/rust-lang/rust/issues/131981), [#131982](https://github.com/rust-lang/rust/issues/131982)). However, it hilariously led to an uncovering of a bug that we were not validating. While this was fixed in [#132209 <small>"Fix validation when lowering ? trait bounds"</small>](https://github.com/rust-lang/rust/pull/132209), we did silently accept this code for a while: ```rust fn test<T: ?Sized<i32>>() {} ``` For the record, this was not unsound or anything. Just nonsense. ### Edition related improvements I also worked on some improvements to items relating to the Rust edition 2024 release, which happens soon. In [#132383 <small>"Implement suggestion for never type fallback lints"</small>](https://github.com/rust-lang/rust/issues/132383), I implemented an improvement to the two lints surrounding never-type fallback changes in Rust 2024. They should allow the automatic application of type annotations that should preserve old never-type fallback behavior in the new edition. ```diff! warning: this function depends on never type fallback being `()` --> $DIR/never-type-fallback-breaking.rs:15:1 | LL | fn m() { | ^^^^^^ | note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:19:17 | LL | true => Default::default(), | ^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + help: use `()` annotations to avoid fallback changes + | + LL | let x: () = match true { + | ++++ ``` Along a similar vein, in Rust 2024 we plan on changing the defaults around `impl Trait` types capturing lifetimes. This [blog post](https://blog.rust-lang.org/2024/09/05/impl-trait-capture-rules.html) explains the rationale, and while it's the more sane default and we expect users not to care much about the change, it can still result in borrow-checker errors. In [#131186 <small>"Try to point out when edition 2024 lifetime capture rules cause borrowck issues"</small>](https://github.com/rust-lang/rust/issues/131186), this implements an additional note to point out what is happening: ```diff error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable --> $DIR/migration-note.rs:20:5 | LL | let a = display_len(&x); | -- immutable borrow occurs here ... LL | x.push(1); | ^^^^^^^^^ mutable borrow occurs here ... LL | println!("{a}"); | --- immutable borrow later used here | + note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules + --> $DIR/migration-note.rs:17:13 + | + LL | let a = display_len(&x); + | ^^^^^^^^^^^^^^^ + help: add a precise capturing bound to avoid overcapturing + | + LL | fn display_len<T>(x: &Vec<T>) -> impl Display + use<T> { + | ++++++++ ``` I also tweaked a confusing error message for `extern {}` blocks in [#131550 <small>"Make some tweaks to extern block diagnostics"</small>](https://github.com/rust-lang/rust/issues/131550) having to do with `unsafe fn` in "unadorned" `extern {}` blocks, and fixed the pretty-printing of the `safe` keyword in [#132088 <small>"Print safety correctly in extern static items"</small>](https://github.com/rust-lang/rust/pull/132088). ### Improvements to return-type notation Another feature I've been working on polishing is "return-type notation". I think it fills an important expressivity gap with the AFIT (async fn in trait) feature I stabilized late last year, and am quite excited to see it eventually go up for stabilization. In [#132194 <small>"Collect item bounds for RPITITs from trait where clauses just like associated types"</small>](https://github.com/rust-lang/rust/pull/132194), I fixed an incongruency between associated types and RPITIT (return-position impl trait in trait) where users can write where-clauses to add additional bounds onto their RPITITs: ```rust trait Foo where Self::method(..): Send, { async fn method(); } ``` While it may not seem super useful, it does give users a (not yet stable) way to add bounds to their `async fn` returned `Future` without needing to desugar it into `impl Future`. This fix also uncovered a subtle bug which led to "shorthand projections" like `T::Assoc` (without a trait) to fail in AFIT trait bounds, which I fixed in [#132373 <small>"Make sure type_param_predicates resolves correctly for RPITIT"</small>](https://github.com/rust-lang/rust/pull/132373). ### Async closures I worked a bit on async closures, fixing how we formatted `for<'a> async Fn()` ([#131657](https://github.com/rust-lang/rust/pull/131657)) bounds, and fixing a bug with coverage ([#131802](https://github.com/rust-lang/rust/pull/131802)). By the end of October, this feature is basically ready for stabilization. ### Unsoundness fixes I fixed a couple of unsoundnesses last month. In [#131201<small> "Disable jump threading UnOp::Not for non-bool"</small>](https://github.com/rust-lang/rust/issues/131201), we realized that the jump threading implementation wasn't treating non-bool types correctly (i.e. integers) for the `Not` operator. I fixed another *nightly-feature-only* unsoundness in [#132151 <small>"Ensure that resume arg outlives region bound for coroutines"</small>](https://github.com/rust-lang/rust/issues/132151), which had to do with lifetime-outlives and the *resume* type of a coroutine. Since coroutines support was built out mostly to support async (and it does so soundly, afaict), it's not totally polished -- this is an area I've [occasionally fixed in the past](https://github.com/rust-lang/rust/pull/119563). ### Raw lifetimes Raw lifetimes are stabilizing soon. They look like `'r#async`, and give people the ability to give lifetimes the names of keywords in the same way that raw identifiers can. When working on the reference guide section for raw lifetimes, a few incongruencies were helpfully pointed out, which I fixed in [#132341 <small>"Reject raw lifetime followed by `'` , like regular lifetimes do"</small>](https://github.com/rust-lang/rust/issues/132341) and [#132363 <small>"Enforce that raw lifetimes must be valid raw identifiers"</small>](https://github.com/rust-lang/rust/issues/132363). ### New trait solver + librarification I've worked a bit on the librarification of the new trait solver, in preparation for its integration into rust-analyzer. In [#131263 <small>"Introduce SolverRelating type relation to the new solver"</small>](https://github.com/rust-lang/rust/issues/131263), I abstracted out the "type relation" used by the new trait solver. This is the API that can be used to tell whether two types are equal and powers type inference. This led to a nice cleanup ([#131343](https://github.com/rust-lang/rust/issues/131343)) in the *old* trait solver's API, too. I worked a bit to improve diagnostics for trait errors in the new trait solver. [#131699 <small>"Try to improve error messages involving aliases in the solver"</small>](https://github.com/rust-lang/rust/issues/131699) sets up the compiler to better deduplicate error messages for associated types, and [#131756 <small>"Deeply normalize TypeTrace when reporting type error in new solver"</small>](https://github.com/rust-lang/rust/issues/131756) helps simplify the types we report in type error messages. Finally, I worked a bit on fixing the type checker to be compatible with the new trait solver. The main issue is that the type checker is not compatible with the "lazy normalization" implemented in the new trait solver. In other words, when we have an associated type that we know how to simplify, like `<[i32; 1] as IntoIterator>::Item` (which is `i32`), we don't always check that it is simplified before structurally matching on that type (e.g. checking that the type is a reference), since the *old* trait solver eagerly simplified associated types whenever it could. A few missing places were fixed in [#131482](https://github.com/rust-lang/rust/issues/131482) and [#131751](https://github.com/rust-lang/rust/issues/131751). ### Diagnostics improvements The theme for diagnostics improvements this month were "fix diagnostic bugs that were annoying myself and others". In [#131754 <small>"Don't report bivariance error when nesting a struct with field errors into another struct"</small>](https://github.com/rust-lang/rust/issues/131754), I was made aware on twitter of spurious errors for malformed structs with `derive()`s on them. That PR suppresses them. [#131702 <small>"Suppress import errors for traits that couldve applied for method lookup error"</small>](https://github.com/rust-lang/rust/issues/131702) suppresses import warnings for cases where there is another method lookup error. For example, when I have: ``` use some_crate::Trait; something.call_method(); ``` where `call_method()` should resolve to `Trait::call_method()` but doesn't apply (e.g. because `something` does not implement it), we should not be reporting an *unused import* warning, since the primary error to fix here is the *method lookup failure*, which will likely fix the import warning after that trait now becomes used. ```diff error[E0599]: no method named `call_method` found for struct `Something` in the current scope --> main.rs:15:9 | 15 | something.call_method(); | ^^^^^^^^^^^ method not found in `Something` | = help: items from traits can only be used if the trait is implemented and in scope note: `Trait` defines an item `call_method`, perhaps you need to implement it --> other_file.rs:7:5 | 7 | pub trait Something { | ^^^^^^^^^^^^^^^^^^^ - warning: unused import: `foo::Bar` - --> src/main.rs:4:5 - | - 4 | use some_crate::Trait; - | ^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(unused_imports)]` on by default ``` [#131549 <small>"Add a note for ? on a impl Future<Output = Result<..>> in sync function"</small>](https://github.com/rust-lang/rust/issues/131549) fixes an incongruency where if you are in a non-async function, and you have a future that yields a `Result` and you try to apply the `?` operator to it, we mention nothing. We already had the machinery to mention `.await`ing it in an async function, but this just implements a note just acknowledging it. In [#131795 <small>"Stop inverting expectation in normalization errors"</small>](https://github.com/rust-lang/rust/issues/131795), I fixed a bug where we were reporting the wrong "expected" and "found" types in an associated type mismatch error. That PR is a common case where in diagnostics where I find that simpler is better. When we begin to special-case things, it often happens that the special casing begins to "bitrot", and users hit corners that the original author never expected. That PR does end up regressing `try {}` block return type error reporting, but I think that needs to be handled in a more sophisticated way, especially since the type inference of `try {}` is likely to need changing in the future. Finally, [#131840 <small>"Dont consider predicates that may hold as impossible in is_impossible_associated_item"</small>](https://github.com/rust-lang/rust/issues/131840) fixes a rustdoc bug where we were documenting trait methods with where clauses with generics, like... ```rust impl Foo { fn needs_sized(&self) where Self: Sized, {} } pub struct Generic<T: ?Sized>(T); impl<T: ?Sized> Foo for Generic<T> {} ``` ...where we were not documenting the method `needs_sized` in the `impl Foo` for `Generic<T>`, since `needs_sized` doesn't hold for *all* `T: ?Sized`. However, this method still *exists*, since I could very easily call it for e.g. `T = String`, or any other sized type. This bug came about because of a change I made a few years ago to not document methods with trivially-unsatisfiable where clauses ([#100221](https://github.com/rust-lang/rust/pull/100221)). However, this didn't consider generic parameters correctly. ### Some compiler cleanups and ICE fixes I worked on a couple handfuls of other cleanups and ICE fixes, but I honestly got too tired to write this section, and it was probably too technical to interest many anyways :smile_cat: