Get basic struct pattern type inference working!
This commit is contained in:
parent
ab5deb7811
commit
3340807bd2
@ -331,8 +331,8 @@ impl_arena_id!(PatId);
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct FieldPat {
|
||||
name: Name,
|
||||
pat: Option<PatId>,
|
||||
pub(crate) name: Name,
|
||||
pub(crate) pat: Option<PatId>,
|
||||
}
|
||||
|
||||
/// Close relative to rustc's hir::PatKind
|
||||
@ -392,7 +392,9 @@ impl Pat {
|
||||
let total_iter = prefix.iter().chain(rest.iter()).chain(suffix.iter());
|
||||
total_iter.map(|pat| *pat).for_each(f);
|
||||
}
|
||||
Pat::Struct { .. } => {} // TODO
|
||||
Pat::Struct { args, .. } => {
|
||||
args.iter().filter_map(|a| a.pat).for_each(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -814,23 +816,20 @@ impl ExprCollector {
|
||||
ast::PatKind::PlaceholderPat(_) => Pat::Wild,
|
||||
ast::PatKind::StructPat(p) => {
|
||||
let path = p.path().and_then(Path::from_ast);
|
||||
let fields = p
|
||||
.field_pat_list()
|
||||
.expect("every struct should have a field list")
|
||||
.field_pats()
|
||||
.into_iter()
|
||||
.map(|f| FieldPat {
|
||||
name: Name::new(f.ident),
|
||||
pat: f.pat.as_ref().map(|p| self.collect_pat(p)),
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(field_list) = p.field_pat_list() {
|
||||
let fields = field_list
|
||||
.field_pats()
|
||||
.into_iter()
|
||||
.map(|f| FieldPat {
|
||||
name: Name::new(f.ident),
|
||||
pat: f.pat.as_ref().map(|p| self.collect_pat(p)),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Pat::Struct {
|
||||
path: path,
|
||||
args: fields,
|
||||
}
|
||||
} else {
|
||||
Pat::Missing
|
||||
Pat::Struct {
|
||||
path: path,
|
||||
args: fields,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ use crate::{
|
||||
db::HirDatabase,
|
||||
type_ref::{TypeRef, Mutability},
|
||||
name::KnownName,
|
||||
expr::{Body, Expr, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement},
|
||||
expr::{Body, Expr, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat},
|
||||
};
|
||||
|
||||
/// The ID of a type variable.
|
||||
@ -872,6 +872,90 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_fields(&self, path: Option<&Path>) -> Option<(Ty, Vec<crate::adt::StructField>)> {
|
||||
let def = path
|
||||
.and_then(|path| self.module.resolve_path(self.db, &path).take_types())
|
||||
.map(|def_id| def_id.resolve(self.db));
|
||||
|
||||
let def = if let Some(def) = def {
|
||||
def
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match def {
|
||||
Def::Struct(s) => {
|
||||
let fields: Vec<_> = self
|
||||
.db
|
||||
.struct_data(s.def_id())
|
||||
.variant_data
|
||||
.fields()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
Some((type_for_struct(self.db, s), fields))
|
||||
}
|
||||
Def::EnumVariant(ev) => {
|
||||
let fields: Vec<_> = ev.variant_data(self.db).fields().iter().cloned().collect();
|
||||
Some((type_for_enum_variant(self.db, ev), fields))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_tuple_struct(&mut self, path: Option<&Path>, sub_pats: &[PatId]) -> Ty {
|
||||
let (ty, fields) = if let Some(x) = self.resolve_fields(path) {
|
||||
x
|
||||
} else {
|
||||
return Ty::Unknown;
|
||||
};
|
||||
|
||||
// walk subpats
|
||||
if fields.len() != sub_pats.len() {
|
||||
return Ty::Unknown;
|
||||
}
|
||||
|
||||
for (&sub_pat, field) in sub_pats.iter().zip(fields.iter()) {
|
||||
let sub_ty = Ty::from_hir(
|
||||
self.db,
|
||||
&self.module,
|
||||
self.impl_block.as_ref(),
|
||||
&field.type_ref,
|
||||
);
|
||||
|
||||
self.infer_pat(sub_pat, &Expectation::has_type(sub_ty));
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_struct(&mut self, path: Option<&Path>, sub_pats: &[FieldPat]) -> Ty {
|
||||
let (ty, fields) = if let Some(x) = self.resolve_fields(path) {
|
||||
x
|
||||
} else {
|
||||
return Ty::Unknown;
|
||||
};
|
||||
|
||||
for sub_pat in sub_pats {
|
||||
let tyref = fields
|
||||
.iter()
|
||||
.find(|field| field.name == sub_pat.name)
|
||||
.map(|field| &field.type_ref);
|
||||
|
||||
if let Some(typeref) = tyref {
|
||||
let sub_ty = Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), typeref);
|
||||
|
||||
if let Some(pat) = sub_pat.pat {
|
||||
self.infer_pat(pat, &Expectation::has_type(sub_ty));
|
||||
} else {
|
||||
// TODO: deal with this case: S { x, y }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
// FIXME: Expectation should probably contain a reference to a Ty instead of
|
||||
// a Ty itself
|
||||
fn infer_pat(&mut self, pat: PatId, expected: &Expectation) -> Ty {
|
||||
@ -900,54 +984,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
path: ref p,
|
||||
args: ref sub_pats,
|
||||
},
|
||||
_expected,
|
||||
) => {
|
||||
let def = p
|
||||
.as_ref()
|
||||
.and_then(|path| self.module.resolve_path(self.db, &path).take_types())
|
||||
.map(|def_id| def_id.resolve(self.db));
|
||||
|
||||
if let Some(def) = def {
|
||||
let (ty, fields) = match def {
|
||||
Def::Struct(s) => {
|
||||
let fields: Vec<_> = self
|
||||
.db
|
||||
.struct_data(s.def_id())
|
||||
.variant_data
|
||||
.fields()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
(type_for_struct(self.db, s), fields)
|
||||
}
|
||||
Def::EnumVariant(ev) => {
|
||||
let fields: Vec<_> =
|
||||
ev.variant_data(self.db).fields().iter().cloned().collect();
|
||||
(type_for_enum_variant(self.db, ev), fields)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// walk subpats
|
||||
if fields.len() == sub_pats.len() {
|
||||
for (&sub_pat, field) in sub_pats.iter().zip(fields.iter()) {
|
||||
let sub_ty = Ty::from_hir(
|
||||
self.db,
|
||||
&self.module,
|
||||
self.impl_block.as_ref(),
|
||||
&field.type_ref,
|
||||
);
|
||||
|
||||
self.infer_pat(sub_pat, &Expectation::has_type(sub_ty));
|
||||
}
|
||||
|
||||
ty
|
||||
} else {
|
||||
expected.ty.clone()
|
||||
}
|
||||
} else {
|
||||
expected.ty.clone()
|
||||
}
|
||||
}
|
||||
_,
|
||||
) => self.infer_tuple_struct(p.as_ref(), sub_pats),
|
||||
(
|
||||
&Pat::Struct {
|
||||
path: ref p,
|
||||
args: ref fields,
|
||||
},
|
||||
_,
|
||||
) => self.infer_struct(p.as_ref(), fields),
|
||||
(_, ref _expected_ty) => expected.ty.clone(),
|
||||
};
|
||||
// use a new type variable if we got Ty::Unknown here
|
||||
|
@ -1,12 +1,11 @@
|
||||
[49; 192) '{ ... }; }': ()
|
||||
[59; 60) 'e': E
|
||||
[63; 76) 'E::A { x: 3 }': E
|
||||
[73; 74) '3': usize
|
||||
[82; 124) 'if let... }': [unknown]
|
||||
[105; 106) 'e': E
|
||||
[107; 124) '{ ... }': [unknown]
|
||||
[117; 118) 'x': [unknown]
|
||||
[130; 189) 'match ... }': [unknown]
|
||||
[136; 137) 'e': E
|
||||
[162; 163) 'x': [unknown]
|
||||
[181; 182) '1': i32
|
||||
[68; 155) '{ ...= e; }': ()
|
||||
[78; 79) 'e': E
|
||||
[82; 95) 'E::A { x: 3 }': E
|
||||
[92; 93) '3': usize
|
||||
[106; 113) 'S(y, z)': S
|
||||
[108; 109) 'y': u32
|
||||
[111; 112) 'z': E
|
||||
[116; 119) 'foo': S
|
||||
[129; 148) 'E::A {..._var }': E
|
||||
[139; 146) 'new_var': usize
|
||||
[151; 152) 'e': E
|
||||
|
Loading…
x
Reference in New Issue
Block a user