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, &'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 Res { 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, ()); + + (::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, ()); + } +}