Skip to content

Semantic check of mut restrictions#159906

Open
CoCo-Japan-pan wants to merge 6 commits into
rust-lang:mainfrom
CoCo-Japan-pan:mut-restriction-check
Open

Semantic check of mut restrictions#159906
CoCo-Japan-pan wants to merge 6 commits into
rust-lang:mainfrom
CoCo-Japan-pan:mut-restriction-check

Conversation

@CoCo-Japan-pan

@CoCo-Japan-pan CoCo-Japan-pan commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

This PR implements semantic checks for mut restrictions proposed in RFC 3323, as part of the related GSoC project.
It lowers field restriction information from HIR into rustc_middle::ty::FieldDef and adds a MIR-level lint that rejects restricted field mutations and struct expressions. As parsing (#156824) and path resolution (#159125) have already been implemented, this PR provides a working implementation of mut restrictions.

Tracking issue: #105077
r? @Urgau
cc @jhpratt

@rustbot

rustbot commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Jul 25, 2026
@rustbot rustbot added the T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. label Jul 25, 2026
@CoCo-Japan-pan CoCo-Japan-pan changed the title Mut restriction check Semantic check of mut restrictions Jul 25, 2026

@Urgau Urgau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start, mostly some tests related comments.

View changes since this review

Comment on lines 935 to 939
if let hir::RestrictionKind::Restricted(path) = impl_restriction.kind {
ty::trait_def::ImplRestrictionKind::Restricted(path.res, impl_restriction.span)
ty::RestrictionKind::Restricted(path.res, impl_restriction.span)
} else {
ty::trait_def::ImplRestrictionKind::Unrestricted
ty::RestrictionKind::Unrestricted
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also make this else-if an match instead, as to be exhaustive.

Comment on lines 415 to 421
// MIR-level lints.
&Lint(check_inline::CheckForceInline),
&Lint(check_call_recursion::CheckCallRecursion),
&Lint(check_packed_ref::CheckPackedRef),
&Lint(check_const_item_mutation::CheckConstItemMutation),
&Lint(check_mut_restriction::CheckMutRestriction),
&Lint(function_item_references::FunctionItemReferences),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Not for this PR) Most of those so called "MIR lints" actually emit hard errors of an lint. We should find another name as it's quite confusing to see a "lint" emit a hard errors.

struct MutRestrictionChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
span: Span,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
span: Span,
mutating_span: Span,

let body_did = self.body.source.instance.def_id();

for (place_base, elem) in place.iter_projections() {
let ProjectionElem::Field(field_idx, _field_ty) = elem else {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some comment about why we are only checking for ProjectionElem::Field, I assume that for an index operation foo.bar[index] we always have a field operation.

};

let base_ty = place_base.ty(self.body, self.tcx);
let ty::Adt(adt_def, _args) = *base_ty.ty.kind() else {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, please add a comment here for why only ty::Adt and why we ignore args.

Comment on lines +2 to +4
//@ revisions: e2015 e2018
//@ [e2015] edition: 2015
//@ [e2018] edition: 2018..

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, no need for both revisions, let's stick 2018 an onward.

//[e2018]~^ ERROR field cannot be mutated outside `crate::foo`
bar.2 = 1; // ok
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also check for inline assembly mutating use.

// for x86-64
asm!(
    "add {0}, 5",
    "mov {1}, 42",
    inout(reg) state.inout_field, // mutable inout_field
    out(reg) state.out_field, // mutable out_field
);

Comment on lines +182 to +188
foo::bar::FooE::Var {
alpha, //[e2015]~ ERROR field cannot be mutated outside `foo::bar`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo::bar`
beta, //[e2015]~ ERROR field cannot be mutated outside `foo`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo`
gamma, // ok
} => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if I someone does alpha: _? Do we report an error?

Should probably also be a test.

Comment on lines +193 to +199
foo::bar::FooE::Tup(
alpha, //[e2015]~ ERROR field cannot be mutated outside `foo::bar`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo::bar`
beta, //[e2015]~ ERROR field cannot be mutated outside `foo`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo`
gamma, // ok
) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also test what happens with an immutable borrowing:

    &foo::bar::FooE::Tup(

Comment on lines +179 to +205
fn change_fooe(foo: &mut foo::bar::FooE) {
match foo {
foo::bar::FooE::Default => {}
foo::bar::FooE::Var {
alpha, //[e2015]~ ERROR field cannot be mutated outside `foo::bar`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo::bar`
beta, //[e2015]~ ERROR field cannot be mutated outside `foo`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo`
gamma, // ok
} => {
*alpha = 1;
*beta = 1;
*gamma = 1;
}
foo::bar::FooE::Tup(
alpha, //[e2015]~ ERROR field cannot be mutated outside `foo::bar`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo::bar`
beta, //[e2015]~ ERROR field cannot be mutated outside `foo`
//[e2018]~^ ERROR field cannot be mutated outside `crate::foo`
gamma, // ok
) => {
*alpha = 1;
*beta = 1;
*gamma = 1;
}
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nadrieril, can you think of another match related tests we should be testing regarding mut-restrictions?

@Urgau Urgau added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 25, 2026
@Urgau

Urgau commented Jul 25, 2026

Copy link
Copy Markdown
Member

cc @cjgillot for the MIR related part since you know more about it than me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants