Try   HackMD

This Month in @compiler-errors (rustc contributions) - December

This is the third(I think?) installment in the "This Month in Errs" series where I try to talk about all the contributions I've done this month. Between the holiday season and getting a pretty bad cold in the middle of the month, it actually turned out a bit more productive than expected.

There wasn't a major theme to my contributions this month, though I did kick off some work (e.g. unsafe binders, and async fn in dyn trait) that I expect to follow through with in 2025. And while I didn't work on it this month, I also expect to put up some stabilization reports, namely return type notation (RTN, #109417) and precise capturing in traits (#130044). Stay tuned.

Async closures

This month was a huge milestone for async closures. Specifically, they're finally stabilized (on nightly), and they're set to land in 1.85[1] #132706 "Stabilize async closures (RFC 3668)". I'm pretty proud with the stabilization report, so give it a read if you'd like.

I believe I mentioned it in last month's blog post, but during the stabilization process, we decided against stabilizing the async Fn() trait bounds syntax. It was moved behind a new feature gate in #132612 "Gate async fn trait bound modifier on async_trait_bounds".

The stabilization uncovered a few bugs, such as some code which failed to consider that the trait for AsyncFnOnce had two associated types (#134017), althrough it doesn't affect stable code. #134933 "Make sure we check the future type is Sized in AsyncFn*" fixed a bug where we failed to properly consider erroneous fn() types like fn() -> dyn Future in the built-in implementations for the AsyncFn traits. The feature was also missing stable MIR support, which was added in #134295 "Encode coroutine-closures in SMIR".

Future work will include making AsyncFn dyn-compatible so they can be boxed (like Box<dyn AsyncFn()>), and working on diagnostics to help point users towards using async closures when they get lifetime errors.

Unsafe binders

I finally sat down and tried to tackle my unsafe binders experiment. If you're unfamiliar with unsafe binders, check out the "unsafe binder types" proposal I wrote up, though I apologize if it's a bit technical. The experiment seeks to provide a more ergonomic (and slightly more coherent, type-theoretically) way to deal with existential lifetimes.

I had originally opened a large PR to implement all of the feature at once. However, this wasn't practical, so I split up the PR. First, #134140 "Add AST support for unsafe binders" implemented the parsing and validation of unsafe binders at a synctactical level. Then, #134625 "Begin to implement type system layer of unsafe binders" implemented the type-checking layer, but we still issue an error if unsafe binders are actually used and we don't lower them to MIR yet.

I'll be working on the final interactions with MIR in #130514 "Implement MIR lowering for unsafe binders". This PR is quite subtle because of the feature's interactions with Drop and the distinction Rust makes between places and values. I expect to clean it up and move it out of draft in the next week or so.

Als, I added Rustdoc support to #134857 "Unsafe binder support in rustdoc". Small piece of the puzzle, but still incremental progress :^)

Async Fn in Dyn Traits (AFIDT)

Another feature implementation I started was "async fn in dyn traits" (AFIDT). This experiment seeks to fill the major missing gap in "async fn in trait" functionality, which is that traits with async fns in them are not dyn-compatible.

Niko wrote a lot about it (starting here). I barely implemented the vague ideas of the feature in
#133122 "Add unpolished, experimental support for AFIDT (async fn in dyn trait)", however, it's clear that it's going to need significant design work to come up with something that is ergonomic, sound, and theoretically self-consistent. I'll be working on that this month hopefully.

Const traits

I may have mentioned it last month, but #133325 "Reimplement ~const trait specialization" fills a necessary gap in the type system to support constifying the PartialEq trait (and others in the standard library) which rely on specialization under the hood. I was then able to open up
#133995 "Constify PartialEq", which is temporarily blocked on a PR that sorts out our "const stability" story a bit better.

One important validation step in Rust is const checking, which validates that the functions called in a const fn or const item are actually legal to be performed in a const context. The PRs #134701 "Correctly note item kind in NonConstFunctionCall error message" and #134732 "Unify conditional-const error reporting with non-const error reporting" tweak error reporting to make const errors a bit more readable as we continue to constify the standard library.

#134951 "Suppress host effect predicates if underlying trait doesn't hold" tweaks error messages to be less redundant when users write ~const and const trait bounds and the underlying trait is not implemented at all.

#133926 "Fix const conditions for RPITITs" fixes support for ~const bounds in return-position impl traits in trait methods, like:

#[const_trait] trait Foo {
    fn method() -> impl ~const Bar;
}

Finally, since we maintain two implementations of const trait checking the old trait solver and the new trait solver there were some discrepancies where the old trait solver wasn't as powerful (i.e., it couldn't prove as many ~const trait bounds). In #134638 "Fix effect predicates from item bounds in old solver" and #134875 "Implement const Destruct in old solver", those discrepancies are slowly being whittled away.

New Features

I worked on a couple other types-related features recently. Most significantly, in #134185 "(Re-)Implement impl_trait_in_bindings", I sketched out a new design for impl_trait_in_bindings, which allows code like:

let x: impl Debug = "hello, world";

I'm not totally sure how useful this feature is, but I expect it to be a good way to assert that a local variable implements a trait, and it can also be used to guide inference for things like closures:

let c: impl Fn(&i32) = |x| {};

The feature also uncovered a funny little bug which I squashed in #134313 "Don't make a def id for impl_trait_in_bindings".

Unrelatedly, I fixed a rustfmt regression due to the default_field_values feature in #134668 "Make sure we don't lose default struct value when formatting struct". The regression highlights an interesting interaction with parser extensions and the formatting we do inside of macros.

New Solver

I worked a bit more on the new trait solver this month. Firstly, I fixed a really funny bug in #133828 "Make sure to record deps from cached task in new solver on first run" which has to do with the fact that we were not tracking incremental dependencies correctly on the first invocation of the trait solver for a specific trait goal, like i32: Debug. Much thanks to @lqd for minimizing the test to something I could actually analyze.

I fixed a bunch of other random new-trait-solver-related bugs. #134742 fixes a bug with impl trait inference and async. In #134744, I suppressed a bug in the transmute validation code. The PR #134746 fixes a bug with autoref coercions and bad associated types. The PR #134771 fixes a bug when an impl provides an invalid type for a const arg. And finally, #134940 fixes a bug when normalizing type-outlives where clauses like where Ty::Assoc: 'a in the borrow-checker.

Diagnostics

#134745 "Normalize each signature input/output in typeck_with_fallback with its own span" tweaks the spans that we use to report errors when users write invalid associated types in their function signatures. This results in duplicated error messages getting de-duplicated, since Rust now sees that they're identical.

The PR #134798 "Make ty::Error implement all auto traits" suppresses unnecessary trait errors when users write structs with fields that have typo'd field types. Hopefully this will help users more clearly see the actionable error message when the make a typo.

The #134945 "Some small nits to the borrowck suggestions for mutating a map through index" makes a few tweaks to the preexisting error logic we have when users try to mutate a HashMap or BTreeMap through the IndexMut operator, like k[v] = i.

Finally, #134639 "Make sure we note ambiguity causes on positive/negative impl conflicts" fixes an inconsistency with #[feature(negative_impls)] where if we have an overlap between a negative impl and a positive (normal) impl, we wouldn't explain why.

Miscellaneous correctness

#133992 "Actually walk into lifetimes and attrs in EarlyContextAndPass" was a fix to the way that the Visitor pattern is implemented for our ASTs. Specifically, we were not fully visiting lifetimes and attributes (#[attr]) correctly. Fixing this bug uncovered some previously untriggered instances (false-negatives) of the HiddenUnicodeCodepoints lint, which detect bad unicode codepoints (like the RTL control characters).

Related to that last lint, #134956 "Account for C string literals and format_args in HiddenUnicodeCodepoints lint" also fixed a bug where we weren't detecting bad unicode codepoints on c"" strings and also in the format string inside of println!("...") and related macros.

The PR #134735 "Consider arm to diverge if guard diverges" is a small ergonomic improvement to our divergence analysis which is responsible for detecting dead code that follows expressions with divering control flow (like return, break, and panic!()). It specifically fixed a case where we didn't consider diverging match guards (like match x { 1 if panic!() => {} }).

Finally, #133889 "Consider fields to be inhabited if they are unstable" fixes a bug introduced by the min_exhaustive_patterns feature stabilization, where although Pin's "inner" pinned field is public, it's unstable, and therefore we probably should consider it the same as private when considering struct inhabitedness.

Edition 2024

This month was major polishing time for the new edition. @ehuss did some amazing work triaging and identifying a ton of bugs having to do with the edition migration lints, and I spent some time working on improving them as a result. I'm incredibly excited for Edition 2024 to land.

The PR #134142 "Rudimentary heuristic to insert parentheses when needed for RPIT overcaptures lint" improves the suggestion to change impl Trait to impl Trait + use<'a, 'b> to consider when parentheses may need to be added for parsing to succeed.

Never type fallback will change in 2024, and I previously discussed improving the error to give better suggestion about where to add () unit type ascription to preserve the old behavior. In #134144 "Properly consider APITs for never type fallback ascription fix", this fixes a bug where we didn't consider the fact that APIT (argument-position impl trait) cannot be turbofished.

Another major change coming in the edition is changes to the way we handle temporary expressions, which the tail_expr_drop_order lint aims to detect. To do so, it changes the way we emit drop statements in MIR to also emit a set of "drop hint" statements at the points where drops would happen in edition 2024, which we can use to do analysis about any major drop changes between editions 2021 and 2024.

However, the code overlooked some codepaths, and we accidentally were affecting the way drops behaved in normal code. This was fixed by #134486 "Make sure we handle backwards_incompatible_lint drops appropriately in drop elaboration" and
#134575 "Handle DropKind::ForLint in coroutines correctly". I'm always appreciative of our users reporting the strangest and coolest bugs to fix, so special thanks to @Clipi-12 for minimizing and reporting the issue in #134482.

The PR #134493 "Always run tail_expr_drop_order lint in promoted MIR query" fixes a bug where cargo check would not fire the tail_expr_drop_order lint, but cargo build would, due to the place in the compiler pipeline that the lint was originally configured to trigger. Moving the analysis further up the pipeline fixed that.

The PR #134478 "Properly record metavar spans for other expansions other than TT" aims to fix some subtleties around error suggestions and macro metavariables (like $expr). This has the side-effect of improving the edition migration lints, but also other suggestions and errors that involve macros as well

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

In the final days before Edition 2024 lands on beta, I put up #134929 "Stabilize style_edition = "2024" in-tree" to make sure that users can specify style_edition = "..." in their rustfmt.toml file. This is important in case they'd like to decouple upgrading to Edition 2024 and reformatting all their code against the new style edition 2024 we are releasing at the same time, since while I wholeheartedly believe that the new style edition is an improvement over the status quo, it may result in a lot of formatting changes in some projects, and they should have the freedom to stage those changes separately if desired.

Miscellaneous ICE fixes

#134635 "Don't ICE on illegal dyn* casts" fixes a bug where we used to panic if we tried to do a nontrivial dyn* to dyn* coercion, such as turning dyn* Trait + Send to dyn* Trait. These are not supported coercions for now, but we weren't expecting them to fail either.

#134103 "Don't ICE when encountering never in range pattern" fixes a bug where we were previously panicking if we encountered a bad range pattern involving a variable whose type is !.

Miscellaneous cleanups

#133768 "Remove generic_associated_types_extended feature gate" removed code that was originally added to the compiler to experiment with dyn-compatible generic associated types, like dyn for<'a> Foo<Assoc<'a> = &'a ()>. However, this feature ended up not making much progress, and maintaining the code was a bit of a hassle. Removing this code also allowed us to remove more dyn Trait special casing in #133872 "No need to create placeholders for GAT args in confirm_object_candidate".

#134141 "Remove more traces of anonymous ADTs" removes some leftover code having to do with the "unnamed_fields" feature that was removed from nightly Rust recently.

#134259 "Clean up infer_return_ty_for_fn_sig" is a minor ergonomic tweak in the control flow for some type lowering code, which previously conceptually prioritied the "recovery" over the "good" path when handling functions signatures with _ in them, like fn foo() -> _ { ... }. It moves all the recovery logic into a new function so it's out of the way.

The PR #134741 "Actually print all the relevant parts of a coroutine in verbose mode" improves the -Zverbose-internals unstable flag to actually print all of the "components" of a coroutine (i.e. its return/yield/resume) types. This is very useful when debugging async and impl trait related bugs.

I tweaked a lot of code in a somewhat disorganized way. #134827 "Some random region tweaks" removes a redundant accessor in the lifetime constraint handling code, and adds an assertion that may catch bugs later in the migration to the new solver. #134491 "Some destructor/drop related tweaks" cleans up some ID handling around Drop/destructor code. And finally, #134984 "ObligationCause construction tweaks in typeck" consolidates some tracking around trait obligations in the type checker.


  1. Coincidentally also when we'll land Rust edition 2024. ↩︎