From cf3fad137ea130a3a9e8baa4419cb171161a3446 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Sat, 11 Jul 2026 12:54:41 +0900 Subject: [PATCH 1/9] Resolve paths for `mut` restrictions --- compiler/rustc_resolve/src/diagnostics.rs | 15 +++++++-- compiler/rustc_resolve/src/late.rs | 38 ++++++++++++++++++++--- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 22cc6a581f19a..565903135b54f 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -8,7 +8,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Spanned, Symbol}; use crate::Res; -use crate::late::PatternSource; +use crate::late::{PatternSource, ResolvingRestrictionKind}; #[derive(Diagnostic)] #[diag("can't use {$is_self -> @@ -542,8 +542,17 @@ pub(crate) struct ExpectedModuleFound { pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); #[derive(Diagnostic)] -#[diag("trait implementation can only be restricted to ancestor modules")] -pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); +#[diag( + "{$kind -> + [impl] trait implementation + *[mut] field mutation +} can only be restricted to ancestor modules" +)] +pub(crate) struct RestrictionAncestorOnly { + #[primary_span] + pub(crate) span: Span, + pub(crate) kind: ResolvingRestrictionKind, +} #[derive(Diagnostic)] #[diag("cannot use a tool module through an import")] diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4ae56ac7c6f3d..8c375f6d26611 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -426,6 +426,23 @@ pub(crate) enum AliasPossibility { Maybe, } +/// Whether resolving `impl` or `mut` restriction paths +#[derive(Debug, Clone, Copy)] +pub(crate) enum ResolvingRestrictionKind { + Impl, + Mut, +} + +impl IntoDiagArg for ResolvingRestrictionKind { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + use std::borrow::Cow; + match self { + ResolvingRestrictionKind::Impl => DiagArgValue::Str(Cow::Borrowed("impl")), + ResolvingRestrictionKind::Mut => DiagArgValue::Str(Cow::Borrowed("mut")), + } + } +} + #[derive(Copy, Clone, Debug)] pub(crate) enum PathSource<'a, 'ast, 'ra> { /// Type paths `Path`. @@ -1489,11 +1506,12 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc ty, is_placeholder: _, default, - mut_restriction: _, + mut_restriction, safety: _, } = f; walk_list!(self, visit_attribute, attrs); try_visit!(self.visit_vis(vis)); + self.resolve_restriction_path(&mut_restriction.kind, ResolvingRestrictionKind::Mut); visit_opt!(self, visit_ident, ident); try_visit!(self.visit_ty(ty)); if let Some(v) = &default { @@ -2864,7 +2882,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ItemKind::Trait(Trait { generics, bounds, items, impl_restriction, .. }) => { // resolve paths for `impl` restrictions - self.resolve_impl_restriction_path(impl_restriction); + self.resolve_restriction_path( + &impl_restriction.kind, + ResolvingRestrictionKind::Impl, + ); // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib( @@ -4480,8 +4501,12 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } - fn resolve_impl_restriction_path(&mut self, restriction: &'ast ast::ImplRestriction) { - match &restriction.kind { + fn resolve_restriction_path( + &mut self, + restriction: &'ast ast::RestrictionKind, + kind: ResolvingRestrictionKind, + ) { + match &restriction { ast::RestrictionKind::Unrestricted => (), ast::RestrictionKind::Restricted { path, id, shorthand: _ } => { self.smart_resolve_path(*id, &None, path, PathSource::Module); @@ -4494,7 +4519,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ) { self.r .dcx() - .create_err(crate::diagnostics::RestrictionAncestorOnly(path.span)) + .create_err(crate::diagnostics::RestrictionAncestorOnly { + span: path.span, + kind, + }) .emit(); } } From 7918a49cd7a5f0e6af5fce64f920d0d971d78362 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Sat, 11 Jul 2026 13:17:57 +0900 Subject: [PATCH 2/9] Add UI tests for path resolution errors of `mut` restrictions --- .../auxiliary/external-mut-restriction.rs | 11 ++ .../restriction-resolution-errors.rs | 64 +++++++++++ .../restriction-resolution-errors.stderr | 107 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 tests/ui/mut-restriction/auxiliary/external-mut-restriction.rs create mode 100644 tests/ui/mut-restriction/restriction-resolution-errors.rs create mode 100644 tests/ui/mut-restriction/restriction-resolution-errors.stderr diff --git a/tests/ui/mut-restriction/auxiliary/external-mut-restriction.rs b/tests/ui/mut-restriction/auxiliary/external-mut-restriction.rs new file mode 100644 index 0000000000000..7b120bfbb3a44 --- /dev/null +++ b/tests/ui/mut-restriction/auxiliary/external-mut-restriction.rs @@ -0,0 +1,11 @@ +#![feature(mut_restriction)] + +pub struct TopLevel { + pub mut(crate) x: i32, +} + +pub mod inner { + pub struct Inner { + pub mut(self) x: i32, + } +} diff --git a/tests/ui/mut-restriction/restriction-resolution-errors.rs b/tests/ui/mut-restriction/restriction-resolution-errors.rs new file mode 100644 index 0000000000000..14b43bfc5ffc8 --- /dev/null +++ b/tests/ui/mut-restriction/restriction-resolution-errors.rs @@ -0,0 +1,64 @@ +//@ aux-build: external-mut-restriction.rs +#![feature(mut_restriction)] + +extern crate external_mut_restriction as external; + +pub mod a { + pub mod b { + pub struct Foo { + pub mut(in a::b) i1: i32, //~ ERROR cannot find module or crate `a` in this scope [E0433] + pub mut(in ::std) i2: i32, //~ ERROR field mutation can only be restricted to ancestor modules + pub mut(in self::c) i3: i32, //~ ERROR field mutation can only be restricted to ancestor modules + pub mut(in super::d) i4: i32, //~ ERROR field mutation can only be restricted to ancestor modules + pub mut(in crate::c) i5: i32, //~ ERROR cannot find module `c` in the crate root [E0433] + pub mut(in super::E) i6: i32, //~ ERROR expected module, found enum `super::E` [E0577] + pub mut(in super::super::super) i7: i32, //~ ERROR too many leading `super` keywords within `crate::a::b` [E0433] + + // OK paths + pub mut(crate) i8: i32, + pub mut(self) i9: i32, + pub mut(super) i10: i32, + pub mut(in crate::a) i11: i32, + pub mut(in super::super) i12: i32, + } + pub mod c {} + } + pub mod d {} + pub enum E {} +} + +pub enum Foo { + Var { + mut(in crate::a) e1: i32, //~ ERROR field mutation can only be restricted to ancestor modules + mut(in crate::a::E) e2: i32, //~ ERROR expected module, found enum `crate::a::E` [E0577] + mut(crate) e3: i32, + mut(self) e4: i32, + mut(super) e5: i32, //~ ERROR too many leading `super` keywords within `crate` [E0433] + }, + Tup(mut(in external) i32), //~ ERROR field mutation can only be restricted to ancestor modules +} + +// Check if we can resolve `use`d paths. +pub mod m1 { + pub union Foo { + pub mut(in crate::m2) x: i32, // OK + } +} +use m1 as m2; + +pub mod m3 { + pub mod m4 { + pub struct Bar ( + pub mut(in crate::m2) i32, //~ ERROR field mutation can only be restricted to ancestor modules + pub mut(in m6) i32, // OK + pub mut(in m6::m5) i32, //~ ERROR field mutation can only be restricted to ancestor modules + pub mut(in m7) i32, //~ ERROR expected module, found enum `m7` [E0577] + ); + use crate::m3 as m6; + use crate::m3::E as m7; + } + mod m5 {} + pub enum E {} +} + +fn main() {} diff --git a/tests/ui/mut-restriction/restriction-resolution-errors.stderr b/tests/ui/mut-restriction/restriction-resolution-errors.stderr new file mode 100644 index 0000000000000..9bf1e4742a619 --- /dev/null +++ b/tests/ui/mut-restriction/restriction-resolution-errors.stderr @@ -0,0 +1,107 @@ +error: field mutation can only be restricted to ancestor modules + --> $DIR/restriction-resolution-errors.rs:10:24 + | +LL | pub mut(in ::std) i2: i32, + | ^^^^^ + +error: field mutation can only be restricted to ancestor modules + --> $DIR/restriction-resolution-errors.rs:11:24 + | +LL | pub mut(in self::c) i3: i32, + | ^^^^^^^ + +error: field mutation can only be restricted to ancestor modules + --> $DIR/restriction-resolution-errors.rs:12:24 + | +LL | pub mut(in super::d) i4: i32, + | ^^^^^^^^ + +error[E0433]: too many leading `super` keywords within `crate::a::b` + --> $DIR/restriction-resolution-errors.rs:15:38 + | +LL | pub mut(in super::super::super) i7: i32, + | ^^^^^ this `super` would go above the crate root + +error: field mutation can only be restricted to ancestor modules + --> $DIR/restriction-resolution-errors.rs:32:16 + | +LL | mut(in crate::a) e1: i32, + | ^^^^^^^^ + +error[E0433]: too many leading `super` keywords within `crate` + --> $DIR/restriction-resolution-errors.rs:36:13 + | +LL | mut(super) e5: i32, + | ^^^^^ this `super` would go above the crate root + +error: field mutation can only be restricted to ancestor modules + --> $DIR/restriction-resolution-errors.rs:38:16 + | +LL | Tup(mut(in external) i32), + | ^^^^^^^^ + +error: field mutation can only be restricted to ancestor modules + --> $DIR/restriction-resolution-errors.rs:52:24 + | +LL | pub mut(in crate::m2) i32, + | ^^^^^^^^^ + +error: field mutation can only be restricted to ancestor modules + --> $DIR/restriction-resolution-errors.rs:54:24 + | +LL | pub mut(in m6::m5) i32, + | ^^^^^^ + +error[E0433]: cannot find module or crate `a` in this scope + --> $DIR/restriction-resolution-errors.rs:9:24 + | +LL | pub mut(in a::b) i1: i32, + | ^ use of unresolved module or unlinked crate `a` + | +help: there is a crate or module with a similar name + | +LL - pub mut(in a::b) i1: i32, +LL + pub mut(in c::b) i1: i32, + | +help: consider importing this module + | +LL + use a; + | + +error[E0433]: cannot find module `c` in the crate root + --> $DIR/restriction-resolution-errors.rs:13:31 + | +LL | pub mut(in crate::c) i5: i32, + | ^ not found in the crate root + +error[E0577]: expected module, found enum `super::E` + --> $DIR/restriction-resolution-errors.rs:14:24 + | +LL | pub mut(in super::E) i6: i32, + | ^^^^^^^^ not a module + +error[E0577]: expected module, found enum `crate::a::E` + --> $DIR/restriction-resolution-errors.rs:33:16 + | +LL | pub mod b { + | --------- similarly named module `b` defined here +... +LL | mut(in crate::a::E) e2: i32, + | ^^^^^^^^^^^ + | +help: a module with a similar name exists + | +LL - mut(in crate::a::E) e2: i32, +LL + mut(in crate::a::b) e2: i32, + | + +error[E0577]: expected module, found enum `m7` + --> $DIR/restriction-resolution-errors.rs:55:24 + | +LL | pub mut(in m7) i32, + | ^^ not a module + +error: aborting due to 14 previous errors + +Some errors have detailed explanations: E0433, E0577. +For more information about an error, try `rustc --explain E0433`. From 3ee2de8361f3fdbf0b131f690620bce91fc83db6 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Sat, 11 Jul 2026 16:41:38 +0900 Subject: [PATCH 3/9] Lower `mut` restriction to HIR --- compiler/rustc_ast_lowering/src/item.rs | 25 +++++++++++++++++++------ compiler/rustc_hir/src/hir.rs | 7 +++++++ compiler/rustc_hir/src/intravisit.rs | 15 ++++++++++++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8ebecb222b940..b75b083076412 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -900,6 +900,7 @@ impl<'hir> LoweringContext<'_, 'hir> { None => Ident::new(sym::integer(index), self.lower_span(f.span)), }, vis_span: self.lower_span(f.vis.span), + mut_restriction: self.lower_mut_restriction(&f.mut_restriction), default: f .default .as_ref() @@ -1795,11 +1796,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - pub(super) fn lower_impl_restriction( - &mut self, - r: &ImplRestriction, - ) -> &'hir hir::ImplRestriction<'hir> { - let kind = match &r.kind { + fn lower_restriction_kind(&mut self, kind: &RestrictionKind) -> hir::RestrictionKind<'hir> { + match kind { RestrictionKind::Unrestricted => hir::RestrictionKind::Unrestricted, RestrictionKind::Restricted { path, id, shorthand: _ } => { let res = self.get_partial_res(*id); @@ -1823,10 +1821,25 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::RestrictionKind::Unrestricted } } - }; + } + } + + pub(super) fn lower_impl_restriction( + &mut self, + r: &ImplRestriction, + ) -> &'hir hir::ImplRestriction<'hir> { + let kind = self.lower_restriction_kind(&r.kind); self.arena.alloc(hir::ImplRestriction { kind, span: self.lower_span(r.span) }) } + pub(super) fn lower_mut_restriction( + &mut self, + r: &MutRestriction, + ) -> &'hir hir::MutRestriction<'hir> { + let kind = self.lower_restriction_kind(&r.kind); + self.arena.alloc(hir::MutRestriction { kind, span: self.lower_span(r.span) }) + } + /// Return the pair of the lowered `generics` as `hir::Generics` and the evaluation of `f` with /// the carried impl trait definitions and bounds. #[instrument(level = "debug", skip(self, f))] diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 236ccca1a517e..f2b47f9ccbcad 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4495,6 +4495,7 @@ pub struct PolyTraitRef<'hir> { pub struct FieldDef<'hir> { pub span: Span, pub vis_span: Span, + pub mut_restriction: &'hir MutRestriction<'hir>, pub ident: Ident, #[stable_hash(ignore)] pub hir_id: HirId, @@ -4752,6 +4753,12 @@ pub struct ImplRestriction<'hir> { pub span: Span, } +#[derive(Debug, Clone, Copy, StableHash)] +pub struct MutRestriction<'hir> { + pub kind: RestrictionKind<'hir>, + pub span: Span, +} + #[derive(Debug, Clone, Copy, StableHash)] pub enum RestrictionKind<'hir> { /// The restriction does not affect the item. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index df410d680f533..5cdf7e920949b 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1405,8 +1405,21 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>( pub fn walk_field_def<'v, V: Visitor<'v>>( visitor: &mut V, - FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _, safety: _ }: &'v FieldDef<'v>, + FieldDef { + hir_id, + ident, + ty, + default, + span: _, + vis_span: _, + mut_restriction, + def_id: _, + safety: _, + }: &'v FieldDef<'v>, ) -> V::Result { + if let RestrictionKind::Restricted(path) = mut_restriction.kind { + walk_list!(visitor, visit_path_segment, path.segments); + } try_visit!(visitor.visit_id(*hir_id)); try_visit!(visitor.visit_ident(*ident)); visit_opt!(visitor, visit_anon_const, default); From 040c434ca587b0fcff31d8581425977f7a671d92 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Sat, 11 Jul 2026 17:06:23 +0900 Subject: [PATCH 4/9] Pretty print HIR's `mut` restriction --- compiler/rustc_hir_pretty/src/lib.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 447ad2447b3b9..d313de7b157da 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -902,6 +902,7 @@ impl<'a> State<'a> { self.commasep(Inconsistent, struct_def.fields(), |s, field| { s.maybe_print_comment(field.span.lo()); s.print_attrs(s.attrs(field.hir_id)); + s.print_mut_restriction(field.mut_restriction); s.print_type(field.ty); }); self.pclose(); @@ -923,6 +924,7 @@ impl<'a> State<'a> { self.hardbreak_if_not_bol(); self.maybe_print_comment(field.span.lo()); self.print_attrs(self.attrs(field.hir_id)); + self.print_mut_restriction(field.mut_restriction); self.print_ident(field.ident); self.word_nbsp(":"); self.print_type(field.ty); @@ -2656,17 +2658,29 @@ impl<'a> State<'a> { } } - fn print_impl_restriction(&mut self, r: &hir::ImplRestriction<'_>) { - match r.kind { + fn print_restriction>>( + &mut self, + k: &hir::RestrictionKind<'_>, + prefix: S, + ) { + match k { hir::RestrictionKind::Unrestricted => {} hir::RestrictionKind::Restricted(path) => { - self.word("impl("); - self.word_nbsp("in"); + self.word(prefix.into()); + self.word_nbsp("(in"); self.print_path(path, false); - self.word(")"); + self.word_nbsp(")"); } } } + + fn print_mut_restriction(&mut self, r: &hir::MutRestriction<'_>) { + self.print_restriction(&r.kind, "mut"); + } + + fn print_impl_restriction(&mut self, r: &hir::ImplRestriction<'_>) { + self.print_restriction(&r.kind, "impl"); + } } /// Does this expression require a semicolon to be treated From 937fadd96e138e76bf31ca287dea48583e0bce2e Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Sat, 11 Jul 2026 18:17:09 +0900 Subject: [PATCH 5/9] Bless UI test for input-stats.rs --- tests/ui/stats/input-stats.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index f4c6808431687..c77b0cb9cb985 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -96,7 +96,7 @@ hir-stats - Trait 256 (NN.N%) 4 hir-stats Attribute 160 (NN.N%) 4 40 hir-stats Variant 144 (NN.N%) 2 72 hir-stats GenericArgs 144 (NN.N%) 3 48 -hir-stats FieldDef 128 (NN.N%) 2 64 +hir-stats FieldDef 144 (NN.N%) 2 72 hir-stats FnDecl 120 (NN.N%) 3 40 hir-stats Stmt 96 (NN.N%) 3 32 hir-stats - Expr 32 (NN.N%) 1 @@ -119,5 +119,5 @@ hir-stats TraitItemId 8 (NN.N%) 2 4 hir-stats ImplItemId 8 (NN.N%) 2 4 hir-stats ForeignItemId 4 (NN.N%) 1 4 hir-stats ---------------------------------------------------------------- -hir-stats Total 8_576 172 +hir-stats Total 8_592 172 hir-stats ================================================================ From 62c026d638b084b636eb3375dd8fffdad97e04ab Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:03:25 +0900 Subject: [PATCH 6/9] Add HIR pretty print tests for `impl` and `mut` restrictions --- tests/pretty/hir-impl-restriction.pp | 15 ++++++++++ tests/pretty/hir-impl-restriction.rs | 12 ++++++++ tests/pretty/hir-mut-restriction.pp | 44 ++++++++++++++++++++++++++++ tests/pretty/hir-mut-restriction.rs | 43 +++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 tests/pretty/hir-impl-restriction.pp create mode 100644 tests/pretty/hir-impl-restriction.rs create mode 100644 tests/pretty/hir-mut-restriction.pp create mode 100644 tests/pretty/hir-mut-restriction.rs diff --git a/tests/pretty/hir-impl-restriction.pp b/tests/pretty/hir-impl-restriction.pp new file mode 100644 index 0000000000000..ce0de88ca7259 --- /dev/null +++ b/tests/pretty/hir-impl-restriction.pp @@ -0,0 +1,15 @@ +#![attr = Feature([impl_restriction#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-impl-restriction.pp + +impl(in crate) trait T1 { } +impl(in self) trait T2 { } + +mod a { + impl(in crate::a) trait T3 { } + impl(in super) trait T4 { } +} diff --git a/tests/pretty/hir-impl-restriction.rs b/tests/pretty/hir-impl-restriction.rs new file mode 100644 index 0000000000000..b9dbe98640037 --- /dev/null +++ b/tests/pretty/hir-impl-restriction.rs @@ -0,0 +1,12 @@ +#![feature(impl_restriction)] +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-impl-restriction.pp + +impl(crate) trait T1 {} +impl(self) trait T2 {} + +mod a { + impl(in crate::a) trait T3 {} + impl(super) trait T4 {} +} diff --git a/tests/pretty/hir-mut-restriction.pp b/tests/pretty/hir-mut-restriction.pp new file mode 100644 index 0000000000000..43364c1784470 --- /dev/null +++ b/tests/pretty/hir-mut-restriction.pp @@ -0,0 +1,44 @@ +#![attr = Feature([mut_restriction#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-mut-restriction.pp + +struct FooS { + mut(in crate) x: i32, + mut(in self) y: i32, +} + +enum FooE { + Var { + mut(in crate) x: i32, + }, + Tup(mut(in self) i32), +} + +union FooU { + mut(in crate) x: i32, + mut(in self) y: i32, +} + +mod a { + struct BarS { + mut(in self) x: i32, + mut(in crate::a) y: i32, + } + struct BazS(mut(in super) i32); + + enum BarE { + Var { + mut(in super) x: i32, + }, + Tup(mut(in crate::a) i32), + } + + union BarU { + mut(in super) x: i32, + mut(in crate::a) y: i32, + } +} diff --git a/tests/pretty/hir-mut-restriction.rs b/tests/pretty/hir-mut-restriction.rs new file mode 100644 index 0000000000000..cb05d4e194dc7 --- /dev/null +++ b/tests/pretty/hir-mut-restriction.rs @@ -0,0 +1,43 @@ +#![feature(mut_restriction)] +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-mut-restriction.pp + +struct FooS { + mut(crate) x: i32, + mut(self) y: i32, +} + +enum FooE { + Var { + mut(crate) x: i32, + }, + Tup(mut(self) i32), +} + +union FooU { + mut(crate) x: i32, + mut(self) y: i32, +} + +mod a { + struct BarS { + mut(self) x: i32, + mut(in crate::a) y: i32, + } + struct BazS( + mut(super) i32, + ); + + enum BarE { + Var { + mut(super) x: i32, + }, + Tup(mut(in crate::a) i32), + } + + union BarU { + mut(super) x: i32, + mut(in crate::a) y: i32, + } +} From c060b6d5f581e21e61e4f06e8b648a37d29a016d Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:39:02 +0900 Subject: [PATCH 7/9] Add pub(..) and unsafe to HIR pretty print tests for `mut` restriction --- tests/pretty/hir-mut-restriction.pp | 2 +- tests/pretty/hir-mut-restriction.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/pretty/hir-mut-restriction.pp b/tests/pretty/hir-mut-restriction.pp index 43364c1784470..e5f101fc3f136 100644 --- a/tests/pretty/hir-mut-restriction.pp +++ b/tests/pretty/hir-mut-restriction.pp @@ -1,4 +1,4 @@ -#![attr = Feature([mut_restriction#0])] +#![attr = Feature([mut_restriction#0, unsafe_fields#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/pretty/hir-mut-restriction.rs b/tests/pretty/hir-mut-restriction.rs index cb05d4e194dc7..938dd8f24b852 100644 --- a/tests/pretty/hir-mut-restriction.rs +++ b/tests/pretty/hir-mut-restriction.rs @@ -1,43 +1,43 @@ -#![feature(mut_restriction)] +#![feature(mut_restriction, unsafe_fields)] //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-mut-restriction.pp struct FooS { - mut(crate) x: i32, - mut(self) y: i32, + pub(crate) mut(crate) x: i32, + mut(self) unsafe y: i32, } enum FooE { Var { - mut(crate) x: i32, + mut(crate) unsafe x: i32, }, Tup(mut(self) i32), } union FooU { - mut(crate) x: i32, + pub mut(crate) unsafe x: i32, mut(self) y: i32, } mod a { struct BarS { - mut(self) x: i32, + pub(super) mut(self) unsafe x: i32, mut(in crate::a) y: i32, } struct BazS( - mut(super) i32, + pub(in crate::a) mut(super) i32, ); enum BarE { Var { - mut(super) x: i32, + mut(super) unsafe x: i32, }, Tup(mut(in crate::a) i32), } union BarU { - mut(super) x: i32, + pub(crate) mut(super) unsafe x: i32, mut(in crate::a) y: i32, } } From b10a1e334e2f2c594d7fa2595e8992fd11f1b6c9 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 17 Dec 2025 11:56:04 -0800 Subject: [PATCH 8/9] compiler: redescribe llvmlike_vector_align as rust_vector_align This is mostly a cosmetic change. The current form of this function tends to mislead people to assume it is implicitly "correct" in various ways by invoking LLVM as an authority. It is only sort-of correct. More specifically, LLVM mostly doesn't care, and neither do we, except when it comes to FFI. So, we have an arbitrary algorithm that works usefully in most cases. It is not inherently more correct than other arbitrary algorithms, so spell this out and weaken its contract. --- compiler/rustc_abi/src/callconv/reg.rs | 2 +- compiler/rustc_abi/src/layout.rs | 4 ++-- compiler/rustc_abi/src/lib.rs | 16 ++++++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 198b62d5fb475..745a2ecfc6159 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -69,7 +69,7 @@ impl Reg { 128 => dl.f128_align, _ => panic!("unsupported float: {self:?}"), }, - RegKind::Vector { .. } => dl.llvmlike_vector_align(self.size), + RegKind::Vector { .. } => dl.rust_vector_align(self.size), } } } diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index ad48e14430952..2218780092287 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1514,7 +1514,7 @@ where BackendRepr::SimdScalableVector { element, count, number_of_vectors }, size.checked_mul(number_of_vectors.0 as u64, dl) .ok_or_else(|| LayoutCalculatorError::SizeOverflow)?, - dl.llvmlike_vector_align(size), + dl.rust_vector_align(size), ), // Non-power-of-two vectors have padding up to the next power-of-two. // If we're a packed repr, remove the padding while keeping the alignment as close @@ -1523,7 +1523,7 @@ where (BackendRepr::Memory { sized: true }, size, Align::max_aligned_factor(size)) } SimdVectorKind::PackedFixed | SimdVectorKind::Fixed => { - (BackendRepr::SimdVector { element, count }, size, dl.llvmlike_vector_align(size)) + (BackendRepr::SimdVector { element, count }, size, dl.rust_vector_align(size)) } }; let size = size.align_to(align); diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index e26b806dc7397..ad745913399d4 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -658,17 +658,25 @@ impl TargetDataLayout { /// psABI-mandated alignment for a vector type, if any #[inline] - fn cabi_vector_align(&self, vec_size: Size) -> Option { + fn c_vector_align(&self, vec_size: Size) -> Option { self.vector_align .iter() .find(|(size, _align)| *size == vec_size) .map(|(_size, align)| *align) } - /// an alignment resembling the one LLVM would pick for a vector + /// Rust-assigned alignment of any vector type + /// + /// When the shape of a vector matches that in a C psABI, we *must* agree when performing FFI. + /// This currently answers correctly for C compatibility purposes as it is a useful default. + /// Otherwise this choice is arbitrary, as vector types do not necessarily match hardware so + /// this can conjure "imaginary" answers that just happen to be convenient for us. + /// + /// Importantly, Rust vector alignment is not required to be monotonic between vector sizes, + /// even though it currently is. #[inline] - pub fn llvmlike_vector_align(&self, vec_size: Size) -> Align { - self.cabi_vector_align(vec_size) + pub fn rust_vector_align(&self, vec_size: Size) -> Align { + self.c_vector_align(vec_size) .unwrap_or(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap()) } From b098a472b4ce1fc01500033563c510643bb8b64c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 11 Jul 2026 17:20:43 -0700 Subject: [PATCH 9/9] Better comment the struct used by `{read,write}_unaligned` --- library/core/src/ptr/mod.rs | 30 ++++++++++--------- tests/mir-opt/pre-codegen/unaligned.rs | 6 ++-- ...d_copy_generic.runtime-optimized.after.mir | 24 +++++++-------- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f458b3e6b68aa..6813c3bfe5859 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1810,22 +1810,22 @@ pub const unsafe fn read(src: *const T) -> T { pub const unsafe fn read_unaligned(src: *const T) -> T { // Always true thanks to the repr, but to demonstrate const { - assert!(mem::offset_of!(Packed::, 0) == 0); - assert!(size_of::() == size_of::>()); + assert!(mem::offset_of!(Unaligned::, 0) == 0); + assert!(size_of::() == size_of::>()); } - let src = src.cast::>(); + let src = src.cast::>(); // SAFETY: the caller must guarantee that `src` is valid for reads. - // Reading it as `Packed` instead of `T` reads those same bytes because + // Reading it as `Unaligned` instead of `T` reads those same bytes because // it's the same size (thus zero offset), but with alignment 1 instead. // // Similarly, because it's the same bytes it's sound to transmute from the - // `Packed` to `T`. Transmute is a value-based (not a place-based) + // `Unaligned` to `T`. Transmute is a value-based (not a place-based) // operation that doesn't care about alignment. unsafe { - let packed = read(src); + let unaligned = read(src); // Can't just destructure because that's not allowed in const fn - mem::transmute_neo(packed) + mem::transmute_neo(unaligned) } } @@ -2020,14 +2020,14 @@ pub const unsafe fn write(dst: *mut T, src: T) { pub const unsafe fn write_unaligned(dst: *mut T, src: T) { // Always true thanks to the repr, but to demonstrate const { - assert!(mem::offset_of!(Packed::, 0) == 0); - assert!(size_of::() == size_of::>()); + assert!(mem::offset_of!(Unaligned::, 0) == 0); + assert!(size_of::() == size_of::>()); } - let dst = dst.cast::>(); - let src = Packed(src); + let dst = dst.cast::>(); + let src = Unaligned(src); // SAFETY: the caller must guarantee that `dst` is valid for writes. - // Writing it as `Packed` instead of `T` writes those same bytes because + // Writing it as `Unaligned` instead of `T` writes those same bytes because // it's the same size (thus zero offset), but with alignment 1 instead. unsafe { write(dst, src) } } @@ -2828,5 +2828,7 @@ pub macro addr_of_mut($place:expr) { &raw mut $place } -#[repr(C, packed)] -struct Packed(T); +/// Used in [`read_unaligned`] and [`write_unaligned`] to load and store `T` +/// with alignment 1 rather than its usual `align_of::()` alignment. +#[repr(Rust, packed)] +struct Unaligned(T); diff --git a/tests/mir-opt/pre-codegen/unaligned.rs b/tests/mir-opt/pre-codegen/unaligned.rs index 4aeb10c261e21..ef59890b27ce6 100644 --- a/tests/mir-opt/pre-codegen/unaligned.rs +++ b/tests/mir-opt/pre-codegen/unaligned.rs @@ -24,11 +24,11 @@ pub unsafe fn unaligned_copy_generic(src: *const T, dst: *mut T) { // CHECK: debug src => _1; // CHECK: debug dst => _2; // CHECK: debug val => [[VAL:_.+]]; - // CHECK: [[SRC_P:_.+]] = copy _1 as *const {{.+}}::Packed (PtrToPtr); + // CHECK: [[SRC_P:_.+]] = copy _1 as *const {{.+}}::Unaligned (PtrToPtr); // CHECK: [[PACKED1:_.+]] = copy (*[[SRC_P]]); // CHECK: [[VAL]] = copy [[PACKED1]] as T (Transmute); - // CHECK: [[DST_P:_.+]] = copy _2 as *mut {{.+}}::Packed (PtrToPtr); - // CHECK: [[PACKED2:_.+]] = {{.+}}::Packed::(copy [[VAL]]); + // CHECK: [[DST_P:_.+]] = copy _2 as *mut {{.+}}::Unaligned (PtrToPtr); + // CHECK: [[PACKED2:_.+]] = {{.+}}::Unaligned::(copy [[VAL]]); // CHECK: (*[[DST_P]]) = copy [[PACKED2]]; // CHECK-NOT: copy_nonoverlapping // CHECK-NOT: drop diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir index 4d0cfa6a4dfa3..ffb1fe334b201 100644 --- a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir @@ -8,46 +8,46 @@ fn unaligned_copy_generic(_1: *const T, _2: *mut T) -> () { scope 1 { debug val => _5; scope 8 (inlined #[track_caller] write_unaligned::) { - let _6: *mut std::ptr::Packed; + let _6: *mut std::ptr::Unaligned; scope 9 { - let _7: std::ptr::Packed; + let _7: std::ptr::Unaligned; scope 10 { - scope 12 (inlined #[track_caller] std::ptr::write::>) { + scope 12 (inlined #[track_caller] std::ptr::write::>) { } } } - scope 11 (inlined std::ptr::mut_ptr::::cast::>) { + scope 11 (inlined std::ptr::mut_ptr::::cast::>) { } } } scope 2 (inlined #[track_caller] read_unaligned::) { - let _3: *const std::ptr::Packed; + let _3: *const std::ptr::Unaligned; scope 3 { - let _4: std::ptr::Packed; + let _4: std::ptr::Unaligned; scope 4 { - scope 7 (inlined transmute_neo::, T>) { + scope 7 (inlined transmute_neo::, T>) { } } - scope 6 (inlined #[track_caller] std::ptr::read::>) { + scope 6 (inlined #[track_caller] std::ptr::read::>) { } } - scope 5 (inlined std::ptr::const_ptr::::cast::>) { + scope 5 (inlined std::ptr::const_ptr::::cast::>) { } } bb0: { StorageLive(_5); StorageLive(_3); - _3 = copy _1 as *const std::ptr::Packed (PtrToPtr); + _3 = copy _1 as *const std::ptr::Unaligned (PtrToPtr); StorageLive(_4); _4 = copy (*_3); _5 = copy _4 as T (Transmute); StorageDead(_4); StorageDead(_3); StorageLive(_6); - _6 = copy _2 as *mut std::ptr::Packed (PtrToPtr); + _6 = copy _2 as *mut std::ptr::Unaligned (PtrToPtr); StorageLive(_7); - _7 = std::ptr::Packed::(copy _5); + _7 = std::ptr::Unaligned::(copy _5); (*_6) = copy _7; StorageDead(_7); StorageDead(_6);