Skip to content

Commit 17f5c4d

Browse files
Fix unit struct/enum variant in destructuring assignment
1 parent 8f36334 commit 17f5c4d

File tree

4 files changed

+80
-7
lines changed

4 files changed

+80
-7
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+37
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
10201020
None
10211021
}
10221022

1023+
/// If the given expression is a path to a unit struct, returns that path.
1024+
/// It is not a complete check, but just tries to reject most paths early
1025+
/// if they are not unit structs.
1026+
/// Type checking will take care of the full validation later.
1027+
fn extract_unit_struct_path<'a>(
1028+
&mut self,
1029+
expr: &'a Expr,
1030+
) -> Option<(&'a Option<QSelf>, &'a Path)> {
1031+
if let ExprKind::Path(qself, path) = &expr.kind {
1032+
// Does the path resolve to something disallowed in a unit struct/variant pattern?
1033+
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
1034+
if partial_res.unresolved_segments() == 0
1035+
&& !partial_res.base_res().expected_in_unit_struct_pat()
1036+
{
1037+
return None;
1038+
}
1039+
}
1040+
return Some((qself, path));
1041+
}
1042+
None
1043+
}
1044+
10231045
/// Convert the LHS of a destructuring assignment to a pattern.
10241046
/// Each sub-assignment is recorded in `assignments`.
10251047
fn destructure_assign(
@@ -1080,6 +1102,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
10801102
return self.pat_without_dbm(lhs.span, tuple_struct_pat);
10811103
}
10821104
}
1105+
// Unit structs and enum variants.
1106+
ExprKind::Path(..) => {
1107+
if let Some((qself, path)) = self.extract_unit_struct_path(lhs) {
1108+
let qpath = self.lower_qpath(
1109+
lhs.id,
1110+
qself,
1111+
path,
1112+
ParamMode::Optional,
1113+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
1114+
);
1115+
// Destructure like a unit struct.
1116+
let unit_struct_pat = hir::PatKind::Path(qpath);
1117+
return self.pat_without_dbm(lhs.span, unit_struct_pat);
1118+
}
1119+
}
10831120
// Structs.
10841121
ExprKind::Struct(se) => {
10851122
let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| {

compiler/rustc_hir/src/def.rs

+5
Original file line numberDiff line numberDiff line change
@@ -657,4 +657,9 @@ impl<Id> Res<Id> {
657657
pub fn expected_in_tuple_struct_pat(&self) -> bool {
658658
matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..))
659659
}
660+
661+
/// Returns whether such a resolved path can occur in a unit struct/variant pattern
662+
pub fn expected_in_unit_struct_pat(&self) -> bool {
663+
matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..))
664+
}
660665
}

compiler/rustc_resolve/src/late.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,10 @@ impl<'a> PathSource<'a> {
309309
) | Res::Local(..)
310310
| Res::SelfCtor(..)
311311
),
312-
PathSource::Pat => matches!(
313-
res,
314-
Res::Def(
315-
DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst,
316-
_,
317-
) | Res::SelfCtor(..)
318-
),
312+
PathSource::Pat => {
313+
res.expected_in_unit_struct_pat()
314+
|| matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
315+
}
319316
PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
320317
PathSource::Struct => matches!(
321318
res,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// check-pass
2+
3+
struct S;
4+
5+
enum E {
6+
V,
7+
}
8+
9+
type A = E;
10+
11+
fn main() {
12+
let mut a;
13+
14+
(S, a) = (S, ());
15+
16+
(E::V, a) = (E::V, ());
17+
18+
(<E>::V, a) = (E::V, ());
19+
(A::V, a) = (E::V, ());
20+
}
21+
22+
impl S {
23+
fn check() {
24+
let a;
25+
(Self, a) = (S, ());
26+
}
27+
}
28+
29+
impl E {
30+
fn check() {
31+
let a;
32+
(Self::V, a) = (E::V, ());
33+
}
34+
}

0 commit comments

Comments
 (0)