Repository: rust-lang/rust
Pull request: `impl` restriction lowering

<!-- homu-ignore:start -->
<!--
If this PR is related to an unstable feature or an otherwise tracked effort,
please link to the relevant tracking issue here. If you don't know of a related
tracking issue or there are none, feel free to ignore this.

This PR will get automatically assigned to a reviewer. In case you would like
a specific user to review your work, you can assign it to them by using

    r? <reviewer name>
-->
<!-- homu-ignore:end -->

This PR is linked to a [GSoC proposal](https://github.com/rust-lang/google-summer-of-code?tab=readme-ov-file#implementing-impl-and-mut-restrictions) and is part of the progress toward implementing `impl` restrictions proposed in [RFC 3323](https://rust-lang.github.io/rfcs/3323-restrictions.html).
This PR implements path resolution for `impl` restrictions. The resolution is performed in `rustc_resolve/src/late.rs` using `smart_resolve_path`.
This PR also checks whether the restricted module or crate is an ancestor. If it is not, it emits a restriction-specific counterpart to visibility’s `AncestorOnly` error.

r? @Urgau 
cc @jhpratt 


Changed files:
filename: compiler/rustc_resolve/src/errors.rs
patch: @@ -560,6 +560,10 @@ pub(crate) struct ExpectedModuleFound {
 #[diag("cannot determine resolution for the visibility", code = E0578)]
 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);
+
 #[derive(Diagnostic)]
 #[diag("cannot use a tool module through an import")]
 pub(crate) struct ToolModuleImported {

filename: compiler/rustc_resolve/src/late.rs
patch: @@ -452,6 +452,8 @@ pub(crate) enum PathSource<'a, 'ast, 'ra> {
     DefineOpaques,
     /// Resolving a macro
     Macro,
+    /// Paths for module or crate root. Used for restrictions.
+    Module,
 }
 
 impl PathSource<'_, '_, '_> {
@@ -460,7 +462,8 @@ impl PathSource<'_, '_, '_> {
             PathSource::Type
             | PathSource::Trait(_)
             | PathSource::Struct(_)
-            | PathSource::DefineOpaques => TypeNS,
+            | PathSource::DefineOpaques
+            | PathSource::Module => TypeNS,
             PathSource::Expr(..)
             | PathSource::Pat
             | PathSource::TupleStruct(..)
@@ -485,7 +488,8 @@ impl PathSource<'_, '_, '_> {
             | PathSource::DefineOpaques
             | PathSource::Delegation
             | PathSource::PreciseCapturingArg(..)
-            | PathSource::Macro => false,
+            | PathSource::Macro
+            | PathSource::Module => false,
         }
     }
 
@@ -528,6 +532,7 @@ impl PathSource<'_, '_, '_> {
             PathSource::ReturnTypeNotation | PathSource::Delegation => "function",
             PathSource::PreciseCapturingArg(..) => "type or const parameter",
             PathSource::Macro => "macro",
+            PathSource::Module => "module",
         }
     }
 
@@ -626,6 +631,7 @@ impl PathSource<'_, '_, '_> {
             ),
             PathSource::PreciseCapturingArg(MacroNS) => false,
             PathSource::Macro => matches!(res, Res::Def(DefKind::Macro(_), _)),
+            PathSource::Module => matches!(res, Res::Def(DefKind::Mod, _)),
         }
     }
 
@@ -646,6 +652,12 @@ impl PathSource<'_, '_, '_> {
             (PathSource::PreciseCapturingArg(..), true) => E0799,
             (PathSource::PreciseCapturingArg(..), false) => E0800,
             (PathSource::Macro, _) => E0425,
+            // FIXME: There is no dedicated error code for this case yet.
+            // E0577 already covers the same situation for visibilities,
+            // so we reuse it here for now. It may make sense to generalize
+            // it for restrictions in the future.
+            (PathSource::Module, true) => E0577,
+            (PathSource::Module, false) => E0433,
         }
     }
 }
@@ -2174,7 +2186,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 | PathSource::Type
                 | PathSource::PreciseCapturingArg(..)
                 | PathSource::ReturnTypeNotation
-                | PathSource::Macro => false,
+                | PathSource::Macro
+                | PathSource::Module => false,
                 PathSource::Expr(..)
                 | PathSource::Pat
                 | PathSource::Struct(_)
@@ -2800,7 +2813,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.diag_metadata.current_impl_items = None;
             }
 
-            ItemKind::Trait(box Trait { generics, bounds, items, .. }) => {
+            ItemKind::Trait(box Trait { generics, bounds, items, impl_restriction, .. }) => {
+                // resolve paths for `impl` restrictions
+                self.resolve_impl_restriction_path(impl_restriction);
+
                 // Create a new rib for the trait-wide type parameters.
                 self.with_generic_param_rib(
                     &generics.params,
@@ -4389,6 +4405,25 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         }
     }
 
+    fn resolve_impl_restriction_path(&mut self, restriction: &'ast ast::ImplRestriction) {
+        match &restriction.kind {
+            ast::RestrictionKind::Unrestricted => (),
+            ast::RestrictionKind::Restricted { path, id, shorthand: _ } => {
+                self.smart_resolve_path(*id, &None, path, PathSource::Module);
+                if let Some(res) = self.r.partial_res_map[&id].full_res()
+                    && let Some(def_id) = res.opt_def_id()
+                {
+                    if !self.r.is_accessible_from(
+                        Visibility::Restricted(def_id),
+                        self.parent_scope.module,
+                    ) {
+                        self.r.dcx().create_err(errors::RestrictionAncestorOnly(path.span)).emit();
+                    }
+                }
+            }
+        }
+    }
+
     // High-level and context dependent path resolution routine.
     // Resolves the path and records the resolution into definition map.
     // If resolution fails tries several techniques to find likely

filename: tests/ui/impl-restriction/restriction_resolution_errors.rs
patch: @@ -0,0 +1,85 @@
+#![feature(impl_restriction)]
+#![expect(incomplete_features)]
+
+pub mod a {
+    pub enum E {}
+    pub mod d {}
+    pub mod b {
+        pub mod c {}
+
+        // We do not use crate-relative paths here, since we follow the
+        // "uniform paths" approach used for type/expression paths.
+        pub impl(in a::b) trait T1 {} //~ ERROR cannot find module or crate `a` in this scope [E0433]
+
+        pub impl(in ::std) trait T2 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+
+        pub impl(in self::c) trait T3 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+
+        pub impl(in super::d) trait T4 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+
+        pub impl(in crate::c) trait T5 {} //~ ERROR cannot find module `c` in the crate root [E0433]
+
+        pub impl(in super::E) trait T6 {} //~ ERROR expected module, found enum `super::E` [E0577]
+
+        pub impl(in super::super::super) trait T7 {} //~ ERROR too many leading `super` keywords [E0433]
+
+        // OK paths
+        pub impl(crate) trait T8 {}
+        pub impl(self) trait T9 {}
+        pub impl(super) trait T10 {}
+        pub impl(in crate::a) trait T11 {}
+        pub impl(in super::super) trait T12 {}
+
+        // Check if we can resolve paths referring to modules declared later.
+        pub impl(in self::f) trait L1 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+
+        pub impl(in super::G) trait L2 {} //~ ERROR expected module, found enum `super::G` [E0577]
+
+        pub impl(in super::h) trait L3 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+
+        pub mod f {}
+    }
+
+    pub enum G {}
+    pub mod h {}
+}
+
+pub impl(in crate::a) trait T13 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+
+pub impl(in crate::a::E) trait T14 {} //~ ERROR expected module, found enum `crate::a::E` [E0577]
+
+pub impl(crate) trait T15 {}
+pub impl(self) trait T16 {}
+
+pub impl(super) trait T17 {} //~ ERROR too many leading `super` keywords [E0433]
+
+// Check if we can resolve paths referring to modules declared later.
+pub impl(in crate::j) trait L4 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+
+pub impl(in crate::I) trait L5 {} //~ ERROR expected module, found enum `crate::I` [E0577]
+
+pub enum I {}
+pub mod j {}
+
+// Check if we can resolve `use`d paths.
+mod m1 {
+    pub impl(in crate::m2) trait U1 {} // OK
+}
+
+use m1 as m2;
+
+mod m3 {
+    mod m4 {
+        pub impl(in crate::m2) trait U2 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+        pub impl(in m6) trait U3 {} // OK
+        pub impl(in m6::m5) trait U4 {} //~ ERROR trait implementation can only be restricted to ancestor modules
+        pub impl(in m7) trait U5 {} //~ 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() {}

filename: tests/ui/impl-restriction/restriction_resolution_errors.stderr
patch: @@ -0,0 +1,140 @@
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:14:21
+   |
+LL |         pub impl(in ::std) trait T2 {}
+   |                     ^^^^^
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:16:21
+   |
+LL |         pub impl(in self::c) trait T3 {}
+   |                     ^^^^^^^
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:18:21
+   |
+LL |         pub impl(in super::d) trait T4 {}
+   |                     ^^^^^^^^
+
+error[E0433]: too many leading `super` keywords
+  --> $DIR/restriction_resolution_errors.rs:24:35
+   |
+LL |         pub impl(in super::super::super) trait T7 {}
+   |                                   ^^^^^ there are too many leading `super` keywords
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:34:21
+   |
+LL |         pub impl(in self::f) trait L1 {}
+   |                     ^^^^^^^
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:38:21
+   |
+LL |         pub impl(in super::h) trait L3 {}
+   |                     ^^^^^^^^
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:47:13
+   |
+LL | pub impl(in crate::a) trait T13 {}
+   |             ^^^^^^^^
+
+error[E0433]: too many leading `super` keywords
+  --> $DIR/restriction_resolution_errors.rs:54:10
+   |
+LL | pub impl(super) trait T17 {}
+   |          ^^^^^ there are too many leading `super` keywords
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:57:13
+   |
+LL | pub impl(in crate::j) trait L4 {}
+   |             ^^^^^^^^
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:73:21
+   |
+LL |         pub impl(in crate::m2) trait U2 {}
+   |                     ^^^^^^^^^
+
+error: trait implementation can only be restricted to ancestor modules
+  --> $DIR/restriction_resolution_errors.rs:75:21
+   |
+LL |         pub impl(in m6::m5) trait U4 {}
+   |                     ^^^^^^
+
+error[E0433]: cannot find module or crate `a` in this scope
+  --> $DIR/restriction_resolution_errors.rs:12:21
+   |
+LL |         pub impl(in a::b) trait T1 {}
+   |                     ^ use of unresolved module or unlinked crate `a`
+   |
+help: there is a crate or module with a similar name
+   |
+LL -         pub impl(in a::b) trait T1 {}
+LL +         pub impl(in c::b) trait T1 {}
+   |
+help: consider importing this module
+   |
+LL +         use a;
+   |
+
+error[E0433]: cannot find module `c` in the crate root
+  --> $DIR/restriction_resolution_errors.rs:20:28
+   |
+LL |         pub impl(in crate::c) trait T5 {}
+   |                            ^ not found in the crate root
+
+error[E0577]: expected module, found enum `super::E`
+  --> $DIR/restriction_resolution_errors.rs:22:21
+   |
+LL |         pub impl(in super::E) trait T6 {}
+   |                     ^^^^^^^^ not a module
+
+error[E0577]: expected module, found enum `super::G`
+  --> $DIR/restriction_resolution_errors.rs:36:21
+   |
+LL |         pub impl(in super::G) trait L2 {}
+   |                     ^^^^^^^^ not a module
+
+error[E0577]: expected module, found enum `crate::a::E`
+  --> $DIR/restriction_resolution_errors.rs:49:13
+   |
+LL |     pub mod b {
+   |     --------- similarly named module `b` defined here
+...
+LL | pub impl(in crate::a::E) trait T14 {}
+   |             ^^^^^^^^^^^
+   |
+help: a module with a similar name exists
+   |
+LL - pub impl(in crate::a::E) trait T14 {}
+LL + pub impl(in crate::a::b) trait T14 {}
+   |
+
+error[E0577]: expected module, found enum `crate::I`
+  --> $DIR/restriction_resolution_errors.rs:59:13
+   |
+LL | pub mod a {
+   | --------- similarly named module `a` defined here
+...
+LL | pub impl(in crate::I) trait L5 {}
+   |             ^^^^^^^^
+   |
+help: a module with a similar name exists
+   |
+LL - pub impl(in crate::I) trait L5 {}
+LL + pub impl(in crate::a) trait L5 {}
+   |
+
+error[E0577]: expected module, found enum `m7`
+  --> $DIR/restriction_resolution_errors.rs:76:21
+   |
+LL |         pub impl(in m7) trait U5 {}
+   |                     ^^ not a module
+
+error: aborting due to 18 previous errors
+
+Some errors have detailed explanations: E0433, E0577.
+For more information about an error, try `rustc --explain E0433`.