Fix unit struct/enum variant in destructuring assignment
This commit is contained in:
parent
8f36334ca9
commit
17f5c4d255
4 changed files with 80 additions and 7 deletions
|
@ -1020,6 +1020,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
None
|
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.
|
/// Convert the LHS of a destructuring assignment to a pattern.
|
||||||
/// Each sub-assignment is recorded in `assignments`.
|
/// Each sub-assignment is recorded in `assignments`.
|
||||||
fn destructure_assign(
|
fn destructure_assign(
|
||||||
|
@ -1080,6 +1102,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
return self.pat_without_dbm(lhs.span, tuple_struct_pat);
|
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.
|
// Structs.
|
||||||
ExprKind::Struct(se) => {
|
ExprKind::Struct(se) => {
|
||||||
let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
|
let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
|
||||||
|
|
|
@ -657,4 +657,9 @@ impl<Id> Res<Id> {
|
||||||
pub fn expected_in_tuple_struct_pat(&self) -> bool {
|
pub fn expected_in_tuple_struct_pat(&self) -> bool {
|
||||||
matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..))
|
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(..))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,13 +309,10 @@ impl<'a> PathSource<'a> {
|
||||||
) | Res::Local(..)
|
) | Res::Local(..)
|
||||||
| Res::SelfCtor(..)
|
| Res::SelfCtor(..)
|
||||||
),
|
),
|
||||||
PathSource::Pat => matches!(
|
PathSource::Pat => {
|
||||||
res,
|
res.expected_in_unit_struct_pat()
|
||||||
Res::Def(
|
|| matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
|
||||||
DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst,
|
}
|
||||||
_,
|
|
||||||
) | Res::SelfCtor(..)
|
|
||||||
),
|
|
||||||
PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
|
PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
|
||||||
PathSource::Struct => matches!(
|
PathSource::Struct => matches!(
|
||||||
res,
|
res,
|
||||||
|
|
|
@ -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, ());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue