Skip to content

Commit

Permalink
Fix unit struct/enum variant in destructuring assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Apr 7, 2022
1 parent 8f36334 commit 17f5c4d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 7 deletions.
37 changes: 37 additions & 0 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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| {
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir/src/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,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(..))
}
}
11 changes: 4 additions & 7 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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, ());
}
}

0 comments on commit 17f5c4d

Please sign in to comment.