Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
055cd27
ptr_cast_slice: add new methods to raw pointers
Paladynee Nov 19, 2025
fcbb046
Add a marker to tell users that there are hidden (deprecated) items i…
GuillaumeGomez Jan 23, 2026
0e240d3
Add GUI regression test for the display and content of the "hidden de…
GuillaumeGomez Jan 23, 2026
c7788af
Fix contrast ratio for Since element in rustodoc dark theme
arferreira Jan 26, 2026
c60ae4d
Add code comment explaining how the CSS selector works for deprecated…
GuillaumeGomez Jan 27, 2026
7e6d3a2
Add section to tests/ui/README.md
reddevilmidzy Jan 18, 2026
500dc3c
Reorder tests/ui/README.md
reddevilmidzy Jan 18, 2026
a085410
Add subdirectory change detection to tidy
reddevilmidzy Jan 1, 2026
890e50d
Stabilize feature(push_mut)
zachs18 Jan 28, 2026
f5f2ca0
Improve move error diagnostic for `AsyncFn` closures
heathdutton Jan 1, 2026
e1417f4
Update `askama` to `0.15.3`
GuillaumeGomez Jan 28, 2026
6eb9c02
Subscribe myself to translation diagnostics
JonathanBrouwer Jan 28, 2026
2a96ea0
Make `QueryDispatcher::Qcx` an associated type
Zalathar Jan 28, 2026
99591e6
Fix -Zmir-enable-passes to detect unregistered enum names
sgasho Jan 24, 2026
8238fa6
Remove or narrow some `typos.toml` allowlist entries
Zalathar Jan 29, 2026
e8bdcb0
Enforce alphabetical sorting in `typos.toml` and tweak comments
Zalathar Jan 29, 2026
38a60a6
Explain some allowlist entries in `typos.toml`
Zalathar Jan 29, 2026
a42bbe6
Fix some typos of "definition"
Zalathar Jan 29, 2026
f5b5368
Fix grammar
ZenPZero Jan 29, 2026
dd43c88
Rollup merge of #150474 - reddevilmidzy:tidy-sub-dir, r=Zalathar
Zalathar Jan 29, 2026
9f551d3
Rollup merge of #150572 - heathdutton:issue-150174-asyncfn-diagnostic…
Zalathar Jan 29, 2026
9f0483e
Rollup merge of #151596 - sgasho:150910_SimplifyCfg_passes_warn, r=nn…
Zalathar Jan 29, 2026
26dfc4d
Rollup merge of #151802 - Zalathar:qcx, r=nnethercote
Zalathar Jan 29, 2026
034b53c
Rollup merge of #149110 - Paladynee:acp/ptr_cast_slice, r=jhpratt
Zalathar Jan 29, 2026
f5822b6
Rollup merge of #151559 - GuillaumeGomez:marker-hidden-deprecated-sea…
Zalathar Jan 29, 2026
3830f76
Rollup merge of #151665 - arferreira:fix-rustdoc-contrast, r=Guillaum…
Zalathar Jan 29, 2026
70e5959
Rollup merge of #151785 - zachs18:stabilize-push_mut, r=jhpratt
Zalathar Jan 29, 2026
a3be677
Rollup merge of #151798 - GuillaumeGomez:update-askama, r=GuillaumeGomez
Zalathar Jan 29, 2026
4d7db90
Rollup merge of #151800 - JonathanBrouwer:subscribe-translation, r=Jo…
Zalathar Jan 29, 2026
a67e345
Rollup merge of #151804 - Zalathar:sort-typos, r=lqd
Zalathar Jan 29, 2026
e397c50
Rollup merge of #151805 - ZenPZero:patch-1, r=jhpratt
Zalathar Jan 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"

[[package]]
name = "askama"
version = "0.15.2"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03341eae1125472b0672fbf35cc9aa7b74cd8e0c3d02f02c28a04678f12aaa7a"
checksum = "10a800c6f7c005e5bcb76ff0b9e61c9e54ad379ce4e83a88ed14ff487a73776d"
dependencies = [
"askama_macros",
"itoa",
Expand All @@ -197,9 +197,9 @@ dependencies = [

[[package]]
name = "askama_derive"
version = "0.15.2"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "461bd78f3da90b5e44eee4272cfb1c4832aa3dcdb6c370aedd3eb253d2b9e3ca"
checksum = "0cb7657165bac49b5c533850e7cd67c1c60059aefc31088f89aa431c8a90d5d9"
dependencies = [
"askama_parser",
"basic-toml",
Expand All @@ -214,18 +214,18 @@ dependencies = [

[[package]]
name = "askama_macros"
version = "0.15.2"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba49fb22ee3074574b8510abd9495d4f0bb9b8f87e8e45ee31e2cee508f7a8e5"
checksum = "e55eacd3e54d32483cd10d0a881a0f28a40f3a763704ac9b8693edc39d7321c7"
dependencies = [
"askama_derive",
]

[[package]]
name = "askama_parser"
version = "0.15.2"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e33eb7484958aaa1f27e9adb556f5d557331cd891bdbb33781bc1f9550b6f6e"
checksum = "20c3df8886ab5acdcd76eee93b3e2df1ef734251438b5b942b5fea22c50d2a0f"
dependencies = [
"rustc-hash 2.1.1",
"serde",
Expand Down
199 changes: 135 additions & 64 deletions compiler/rustc_borrowck/src/diagnostics/move_errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diag};
use rustc_hir::intravisit::Visitor;
Expand All @@ -7,7 +8,7 @@ use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
use rustc_span::def_id::DefId;
use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::debug;
Expand Down Expand Up @@ -472,49 +473,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if def_id.as_local() == Some(self.mir_def_id())
&& let Some(upvar_field) = upvar_field =>
{
let closure_kind_ty = closure_args.as_closure().kind_ty();
let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
Some(ty::ClosureKind::FnOnce) => {
bug!("closure kind does not match first argument type")
}
None => bug!("closure kind not inferred by borrowck"),
};
let capture_description =
format!("captured variable in an `{closure_kind}` closure");

let upvar = &self.upvars[upvar_field.index()];
let upvar_hir_id = upvar.get_root_variable();
let upvar_name = upvar.to_string(tcx);
let upvar_span = tcx.hir_span(upvar_hir_id);

let place_name = self.describe_any_place(move_place.as_ref());

let place_description =
if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
format!("{place_name}, a {capture_description}")
} else {
format!("{place_name}, as `{upvar_name}` is a {capture_description}")
};

debug!(
"report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
closure_kind_ty, closure_kind, place_description,
);

let closure_span = tcx.def_span(def_id);

self.cannot_move_out_of(span, &place_description)
.with_span_label(upvar_span, "captured outer variable")
.with_span_label(
closure_span,
format!("captured by this `{closure_kind}` closure"),
)
.with_span_help(
self.get_closure_bound_clause_span(*def_id),
"`Fn` and `FnMut` closures require captured values to be able to be \
consumed multiple times, but `FnOnce` closures may consume them only once",
)
self.report_closure_move_error(
span,
move_place,
*def_id,
closure_args.as_closure().kind_ty(),
upvar_field,
ty::Asyncness::No,
)
}
ty::CoroutineClosure(def_id, closure_args)
if def_id.as_local() == Some(self.mir_def_id())
&& let Some(upvar_field) = upvar_field
&& self
.get_closure_bound_clause_span(*def_id, ty::Asyncness::Yes)
.is_some() =>
{
self.report_closure_move_error(
span,
move_place,
*def_id,
closure_args.as_coroutine_closure().kind_ty(),
upvar_field,
ty::Asyncness::Yes,
)
}
_ => {
let source = self.borrowed_content_source(deref_base);
Expand Down Expand Up @@ -563,45 +545,134 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
err
}

fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
fn report_closure_move_error(
&self,
span: Span,
move_place: Place<'tcx>,
def_id: DefId,
closure_kind_ty: Ty<'tcx>,
upvar_field: FieldIdx,
asyncness: ty::Asyncness,
) -> Diag<'infcx> {
let tcx = self.infcx.tcx;

let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
Some(ty::ClosureKind::FnOnce) => {
bug!("closure kind does not match first argument type")
}
None => bug!("closure kind not inferred by borrowck"),
};

let async_prefix = if asyncness.is_async() { "Async" } else { "" };
let capture_description =
format!("captured variable in an `{async_prefix}{closure_kind}` closure");

let upvar = &self.upvars[upvar_field.index()];
let upvar_hir_id = upvar.get_root_variable();
let upvar_name = upvar.to_string(tcx);
let upvar_span = tcx.hir_span(upvar_hir_id);

let place_name = self.describe_any_place(move_place.as_ref());

let place_description = if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
format!("{place_name}, a {capture_description}")
} else {
format!("{place_name}, as `{upvar_name}` is a {capture_description}")
};

debug!(?closure_kind_ty, ?closure_kind, ?place_description);

let closure_span = tcx.def_span(def_id);

let help_msg = format!(
"`{async_prefix}Fn` and `{async_prefix}FnMut` closures require captured values to \
be able to be consumed multiple times, but `{async_prefix}FnOnce` closures may \
consume them only once"
);

let mut err = self
.cannot_move_out_of(span, &place_description)
.with_span_label(upvar_span, "captured outer variable")
.with_span_label(
closure_span,
format!("captured by this `{async_prefix}{closure_kind}` closure"),
);

if let Some(bound_span) = self.get_closure_bound_clause_span(def_id, asyncness) {
err.span_help(bound_span, help_msg);
} else if !asyncness.is_async() {
// For sync closures, always emit the help message even without a span.
// For async closures, we only enter this branch if we found a valid span
// (due to the match guard), so no fallback is needed.
err.help(help_msg);
}

err
}

fn get_closure_bound_clause_span(
&self,
def_id: DefId,
asyncness: ty::Asyncness,
) -> Option<Span> {
let tcx = self.infcx.tcx;
let typeck_result = tcx.typeck(self.mir_def_id());
// Check whether the closure is an argument to a call, if so,
// get the instantiated where-bounds of that call.
let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return None };

let predicates = match parent.kind {
hir::ExprKind::Call(callee, _) => {
let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
let ty = typeck_result.node_type_opt(callee.hir_id)?;
let ty::FnDef(fn_def_id, args) = ty.kind() else { return None };
tcx.predicates_of(fn_def_id).instantiate(tcx, args)
}
hir::ExprKind::MethodCall(..) => {
let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
return DUMMY_SP;
};
let (_, method) = typeck_result.type_dependent_def(parent.hir_id)?;
let args = typeck_result.node_args(parent.hir_id);
tcx.predicates_of(method).instantiate(tcx, args)
}
_ => return DUMMY_SP,
_ => return None,
};

// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`
// or `AsyncFn[Mut]`.
for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
if let Some(clause) = pred.as_trait_clause()
&& let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
&& *clause_closure_def_id == def_id
&& (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
|| tcx.lang_items().fn_trait() == Some(clause.def_id()))
{
// Found `<TyOfCapturingClosure as FnMut>`
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
// could be changed to `FnOnce()` to avoid the move error.
return *span;
let dominated_by_fn_trait = self
.closure_clause_kind(*pred, def_id, asyncness)
.is_some_and(|kind| matches!(kind, ty::ClosureKind::Fn | ty::ClosureKind::FnMut));
if dominated_by_fn_trait {
// Found `<TyOfCapturingClosure as FnMut>` or
// `<TyOfCapturingClosure as AsyncFnMut>`.
// We point at the bound that coerced the closure, which could be changed
// to `FnOnce()` or `AsyncFnOnce()` to avoid the move error.
return Some(*span);
}
}
DUMMY_SP
None
}

/// If `pred` is a trait clause binding the closure `def_id` to `Fn`/`FnMut`/`FnOnce`
/// (or their async equivalents based on `asyncness`), returns the corresponding
/// `ClosureKind`. Otherwise returns `None`.
fn closure_clause_kind(
&self,
pred: ty::Clause<'tcx>,
def_id: DefId,
asyncness: ty::Asyncness,
) -> Option<ty::ClosureKind> {
let tcx = self.infcx.tcx;
let clause = pred.as_trait_clause()?;
let kind = match asyncness {
ty::Asyncness::Yes => tcx.async_fn_trait_kind_from_def_id(clause.def_id()),
ty::Asyncness::No => tcx.fn_trait_kind_from_def_id(clause.def_id()),
}?;
match clause.self_ty().skip_binder().kind() {
ty::Closure(id, _) | ty::CoroutineClosure(id, _) if *id == def_id => Some(kind),
_ => None,
}
}

fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
Expand Down
30 changes: 21 additions & 9 deletions compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,32 @@ macro_rules! declare_passes {
)+
)*

static PASS_NAMES: LazyLock<FxIndexSet<&str>> = LazyLock::new(|| [
static PASS_NAMES: LazyLock<FxIndexSet<&str>> = LazyLock::new(|| {
let mut set = FxIndexSet::default();
// Fake marker pass
"PreCodegen",
set.insert("PreCodegen");
$(
$(
stringify!($pass_name),
$(
$(
$mod_name::$pass_name::$ident.name(),
)*
)?
set.extend(pass_names!($mod_name : $pass_name $( { $($ident),* } )? ));
)+
)*
].into_iter().collect());
set
});
};
}

macro_rules! pass_names {
// pass groups: only pass names inside are considered pass_names
($mod_name:ident : $pass_group:ident { $($pass_name:ident),* $(,)? }) => {
[
$(
$mod_name::$pass_group::$pass_name.name(),
)*
]
};
// lone pass names: stringify the struct or enum name
($mod_name:ident : $pass_name:ident) => {
[stringify!($pass_name)]
};
}

Expand Down
15 changes: 5 additions & 10 deletions compiler/rustc_query_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDA

// This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`.
impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool>
QueryDispatcher<QueryCtxt<'tcx>>
for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
QueryDispatcher for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
where
for<'a> C::Key: HashStable<StableHashingContext<'a>>,
{
type Qcx = QueryCtxt<'tcx>;
type Key = C::Key;
type Value = C::Value;
type Cache = C;
Expand Down Expand Up @@ -104,10 +104,7 @@ where
}

#[inline(always)]
fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache
where
'tcx: 'a,
{
fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache {
// Safety:
// This is just manually doing the subfield referencing through pointer math.
unsafe {
Expand Down Expand Up @@ -215,15 +212,13 @@ where
/// on the type `rustc_query_impl::query_impl::$name::QueryType`.
trait QueryDispatcherUnerased<'tcx> {
type UnerasedValue;
type Dispatcher: QueryDispatcher<QueryCtxt<'tcx>>;
type Dispatcher: QueryDispatcher<Qcx = QueryCtxt<'tcx>>;

const NAME: &'static &'static str;

fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher;

fn restore_val(
value: <Self::Dispatcher as QueryDispatcher<QueryCtxt<'tcx>>>::Value,
) -> Self::UnerasedValue;
fn restore_val(value: <Self::Dispatcher as QueryDispatcher>::Value) -> Self::UnerasedValue;
}

pub fn query_system<'a>(
Expand Down
Loading
Loading