about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs37
-rw-r--r--compiler/rustc_hir/src/def.rs5
-rw-r--r--compiler/rustc_resolve/src/late.rs11
-rw-r--r--src/test/ui/destructuring-assignment/struct-or-enum-variant-path.rs34
4 files changed, 80 insertions, 7 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 5c3e3be2116..a1d994c2f90 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1020,6 +1020,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
         None
     }
 
+    /// If the given expression is a path to a unit struct, returns that path.
+    /// It is not a complete check, but just tries to reject most paths early
+    /// if they are not unit structs.
+    /// Type checking will take care of the full validation later.
+    fn extract_unit_struct_path<'a>(
+        &mut self,
+        expr: &'a Expr,
+    ) -> Option<(&'a Option<QSelf>, &'a Path)> {
+        if let ExprKind::Path(qself, path) = &expr.kind {
+            // Does the path resolve to something disallowed in a unit struct/variant pattern?
+            if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
+                if partial_res.unresolved_segments() == 0
+                    && !partial_res.base_res().expected_in_unit_struct_pat()
+                {
+                    return None;
+                }
+            }
+            return Some((qself, path));
+        }
+        None
+    }
+
     /// Convert the LHS of a destructuring assignment to a pattern.
     /// Each sub-assignment is recorded in `assignments`.
     fn destructure_assign(
@@ -1080,6 +1102,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     return self.pat_without_dbm(lhs.span, tuple_struct_pat);
                 }
             }
+            // Unit structs and enum variants.
+            ExprKind::Path(..) => {
+                if let Some((qself, path)) = self.extract_unit_struct_path(lhs) {
+                    let qpath = self.lower_qpath(
+                        lhs.id,
+                        qself,
+                        path,
+                        ParamMode::Optional,
+                        ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+                    );
+                    // Destructure like a unit struct.
+                    let unit_struct_pat = hir::PatKind::Path(qpath);
+                    return self.pat_without_dbm(lhs.span, unit_struct_pat);
+                }
+            }
             // Structs.
             ExprKind::Struct(se) => {
                 let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 324e1100057..7416ad79aef 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -663,4 +663,9 @@ impl<Id> Res<Id> {
     pub fn expected_in_tuple_struct_pat(&self) -> bool {
         matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..))
     }
+
+    /// Returns whether such a resolved path can occur in a unit struct/variant pattern
+    pub fn expected_in_unit_struct_pat(&self) -> bool {
+        matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..))
+    }
 }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index ab353128cbc..3db4d4481b4 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -396,13 +396,10 @@ impl<'a> PathSource<'a> {
                 ) | Res::Local(..)
                     | Res::SelfCtor(..)
             ),
-            PathSource::Pat => matches!(
-                res,
-                Res::Def(
-                    DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst,
-                    _,
-                ) | Res::SelfCtor(..)
-            ),
+            PathSource::Pat => {
+                res.expected_in_unit_struct_pat()
+                    || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
+            }
             PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
             PathSource::Struct => matches!(
                 res,
diff --git a/src/test/ui/destructuring-assignment/struct-or-enum-variant-path.rs b/src/test/ui/destructuring-assignment/struct-or-enum-variant-path.rs
new file mode 100644
index 00000000000..8da7f90c524
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/struct-or-enum-variant-path.rs
@@ -0,0 +1,34 @@
+// check-pass
+
+struct S;
+
+enum E {
+    V,
+}
+
+type A = E;
+
+fn main() {
+    let mut a;
+
+    (S, a) = (S, ());
+
+    (E::V, a) = (E::V, ());
+
+    (<E>::V, a) = (E::V, ());
+    (A::V, a) = (E::V, ());
+}
+
+impl S {
+    fn check() {
+        let a;
+        (Self, a) = (S, ());
+    }
+}
+
+impl E {
+    fn check() {
+        let a;
+        (Self::V, a) = (E::V, ());
+    }
+}